Improve zones UI (#4576)

* Improve zones UI

* Make home blue

* Only fit after edit in dialog

* Filter states from UI edtor

* Comments

* bring editable circle to front

* Update ha-config-zone.ts

* Update ha-config-zone.ts

* Hide side navigation for zones

* Fix initial ignore fit, fix click on switch label, space dialog buttons
This commit is contained in:
Bram Kragten 2020-01-24 23:21:26 +01:00 committed by GitHub
parent 1f38d13b3b
commit b7a3fe6e91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 404 additions and 136 deletions

View File

@ -0,0 +1,12 @@
export const addDistanceToCoord = (
location: [number, number],
dx: number,
dy: number
): [number, number] => {
const rEarth = 6378000;
const newLatitude = location[0] + (dy / rEarth) * (180 / Math.PI);
const newLongitude =
location[1] +
((dx / rEarth) * (180 / Math.PI)) / Math.cos((location[0] * Math.PI) / 180);
return [newLatitude, newLongitude];
};

View File

@ -239,6 +239,7 @@ export class HaAreaDevicesPicker extends SubscribeMixin(LitElement) {
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (changedProps.has("area") && this.area) {
this._areaPicker = true;
this.value = this.area;

View File

@ -0,0 +1,28 @@
import { customElement, CSSResult, css } from "lit-element";
import "@material/mwc-dialog";
import { style } from "@material/mwc-dialog/mwc-dialog-css";
// tslint:disable-next-line
import { Dialog } from "@material/mwc-dialog";
import { Constructor } from "../types";
// tslint:disable-next-line
const MwcDialog = customElements.get("mwc-dialog") as Constructor<Dialog>;
@customElement("ha-dialog")
export class HaDialog extends MwcDialog {
protected static get styles(): CSSResult[] {
return [
style,
css`
.mdc-dialog__actions {
justify-content: var(--justify-action-buttons, flex-end);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-dialog": HaDialog;
}
}

View File

@ -21,6 +21,7 @@ export class HaSwitch extends MwcSwitch {
"slotted",
Boolean(this._slot.assignedNodes().length)
);
this._slot.addEventListener("click", () => (this.checked = !this.checked));
}
protected static get styles(): CSSResult[] {

View File

@ -42,7 +42,12 @@ class LocationEditor extends LitElement {
if (!this._leafletMap || !this.location) {
return;
}
this._leafletMap.setView(this.location, this.fitZoom);
if ((this._locationMarker as Circle).getBounds) {
this._leafletMap.fitBounds((this._locationMarker as Circle).getBounds());
} else {
this._leafletMap.setView(this.location, this.fitZoom);
}
this._ignoreFitToMap = this.location;
}
protected render(): TemplateResult | void {
@ -74,7 +79,6 @@ class LocationEditor extends LitElement {
) {
this.fitMap();
}
this._ignoreFitToMap = undefined;
}
if (changedProps.has("radius")) {
this._updateRadius();
@ -234,6 +238,7 @@ class LocationEditor extends LitElement {
height: 100%;
}
.leaflet-edit-move {
border-radius: 50%;
cursor: move !important;
}
.leaflet-edit-resize {

View File

@ -32,18 +32,20 @@ declare global {
}
}
export interface Location {
export interface MarkerLocation {
latitude: number;
longitude: number;
radius: number;
name: string;
radius?: number;
name?: string;
id: string;
icon: string;
icon?: string;
radius_color?: string;
editable?: boolean;
}
@customElement("ha-locations-editor")
export class HaLocationsEditor extends LitElement {
@property() public locations?: Location[];
@property() public locations?: MarkerLocation[];
public fitZoom = 16;
// tslint:disable-next-line
@ -51,6 +53,7 @@ export class HaLocationsEditor extends LitElement {
// tslint:disable-next-line
private _leafletMap?: Map;
private _locationMarkers?: { [key: string]: Marker | Circle };
private _circles: { [key: string]: Circle } = {};
public fitMap(): void {
if (
@ -74,7 +77,17 @@ export class HaLocationsEditor extends LitElement {
if (!marker) {
return;
}
this._leafletMap.setView(marker.getLatLng(), this.fitZoom);
if ((marker as Circle).getBounds) {
this._leafletMap.fitBounds((marker as Circle).getBounds());
(marker as Circle).bringToFront();
} else {
const circle = this._circles[id];
if (circle) {
this._leafletMap.fitBounds(circle.getBounds());
} else {
this._leafletMap.setView(marker.getLatLng(), this.fitZoom);
}
}
}
protected render(): TemplateResult | void {
@ -155,6 +168,9 @@ export class HaLocationsEditor extends LitElement {
marker.remove();
});
this._locationMarkers = undefined;
Object.values(this._circles).forEach((circle) => circle.remove());
this._circles = {};
}
if (!this.locations || !this.locations.length) {
@ -163,60 +179,69 @@ export class HaLocationsEditor extends LitElement {
this._locationMarkers = {};
this.locations.forEach((location: Location) => {
this.locations.forEach((location: MarkerLocation) => {
let icon: DivIcon | undefined;
if (location.icon) {
// create icon
let iconHTML = "";
const el = document.createElement("ha-icon");
el.setAttribute("icon", location.icon);
iconHTML = el.outerHTML;
const el = document.createElement("div");
el.className = "named-icon";
if (location.name) {
el.innerText = location.name;
}
const iconEl = document.createElement("ha-icon");
iconEl.setAttribute("icon", location.icon);
el.prepend(iconEl);
icon = this.Leaflet!.divIcon({
html: iconHTML,
html: el.outerHTML,
iconSize: [24, 24],
className: "light leaflet-edit-move",
className: "light",
});
}
if (location.radius) {
const circle = this.Leaflet!.circle(
[location.latitude, location.longitude],
{
color: "#FF9800",
color: location.radius_color ? location.radius_color : "#FF9800",
radius: location.radius,
}
);
// @ts-ignore
circle.editing.enable();
circle.addTo(this._leafletMap!);
// @ts-ignore
const moveMarker = circle.editing._moveMarker;
// @ts-ignore
const resizeMarker = circle.editing._resizeMarkers[0];
if (icon) {
moveMarker.setIcon(icon);
}
resizeMarker.id = moveMarker.id = location.id;
moveMarker
.addEventListener(
if (location.editable) {
// @ts-ignore
circle.editing.enable();
// @ts-ignore
const moveMarker = circle.editing._moveMarker;
// @ts-ignore
const resizeMarker = circle.editing._resizeMarkers[0];
if (icon) {
moveMarker.setIcon(icon);
}
resizeMarker.id = moveMarker.id = location.id;
moveMarker
.addEventListener(
"dragend",
// @ts-ignore
(ev: DragEndEvent) => this._updateLocation(ev)
)
.addEventListener(
"click",
// @ts-ignore
(ev: MouseEvent) => this._markerClicked(ev)
);
resizeMarker.addEventListener(
"dragend",
// @ts-ignore
(ev: DragEndEvent) => this._updateLocation(ev)
)
.addEventListener(
"click",
// @ts-ignore
(ev: MouseEvent) => this._markerClicked(ev)
(ev: DragEndEvent) => this._updateRadius(ev)
);
resizeMarker.addEventListener(
"dragend",
// @ts-ignore
(ev: DragEndEvent) => this._updateRadius(ev)
);
this._locationMarkers![location.id] = circle;
} else {
this._locationMarkers![location.id] = circle;
} else {
this._circles[location.id] = circle;
}
}
if (!location.radius || !location.editable) {
const options: MarkerOptions = {
draggable: true,
draggable: Boolean(location.editable),
title: location.name,
};
@ -255,13 +280,20 @@ export class HaLocationsEditor extends LitElement {
#map {
height: 100%;
}
.leaflet-edit-move {
.leaflet-marker-draggable {
cursor: move !important;
}
.leaflet-edit-resize {
border-radius: 50%;
cursor: nesw-resize !important;
}
.named-icon {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
text-align: center;
}
`;
}
}

View File

@ -4,8 +4,8 @@ export interface Zone {
id: string;
name: string;
icon?: string;
latitude?: number;
longitude?: number;
latitude: number;
longitude: number;
passive?: boolean;
radius?: number;
}

View File

@ -29,6 +29,8 @@ declare global {
}
}
const NO_SIDEBAR_PAGES = ["zone"];
@customElement("ha-panel-config")
class HaPanelConfig extends LitElement {
@property() public hass!: HomeAssistant;
@ -95,8 +97,9 @@ class HaPanelConfig extends LitElement {
const isWide =
this.hass.dockedSidebar === "docked" ? this._wideSidebar : this._wide;
const showSidebar = isWide && !NO_SIDEBAR_PAGES.includes(curPage);
return html`
${isWide
${showSidebar
? html`
<div class="side-bar">
<div class="toolbar">Configuration</div>
@ -137,7 +140,7 @@ class HaPanelConfig extends LitElement {
.wideSidebar=${this._wideSidebar}
.showAdvanced=${this._showAdvanced}
.cloudStatus=${this._cloudStatus}
class=${classMap({ "wide-config": isWide })}
class=${classMap({ "wide-config": showSidebar })}
></ha-config-router>
`;
}

View File

@ -10,10 +10,10 @@ import memoizeOne from "memoize-one";
import "@polymer/paper-input/paper-input";
import "@material/mwc-button";
import "@material/mwc-dialog";
import "../../../components/entity/ha-entities-picker";
import "../../../components/user/ha-user-picker";
import "../../../components/ha-dialog";
import { PersonDetailDialogParams } from "./show-dialog-person-detail";
import { PolymerChangedEvent } from "../../../polymer-types";
import { HomeAssistant } from "../../../types";
@ -56,7 +56,7 @@ class DialogPersonDetail extends LitElement {
}
const nameInvalid = this._name.trim() === "";
return html`
<mwc-dialog
<ha-dialog
open
@closing="${this._close}"
.title=${this._params.entry
@ -162,7 +162,7 @@ class DialogPersonDetail extends LitElement {
? this.hass!.localize("ui.panel.config.person.detail.update")
: this.hass!.localize("ui.panel.config.person.detail.create")}
</mwc-button>
</mwc-dialog>
</ha-dialog>
`;
}
@ -224,10 +224,11 @@ class DialogPersonDetail extends LitElement {
static get styles(): CSSResult[] {
return [
css`
mwc-dialog {
ha-dialog {
min-width: 400px;
max-width: 600px;
--mdc-dialog-title-ink-color: var(--primary-text-color);
--justify-action-buttons: space-between;
}
.form {
padding-bottom: 24px;

View File

@ -9,14 +9,15 @@ import {
import "@polymer/paper-input/paper-input";
import "@material/mwc-button";
import "@material/mwc-dialog";
import "../../../components/map/ha-location-editor";
import "../../../components/ha-switch";
import "../../../components/ha-dialog";
import { ZoneDetailDialogParams } from "./show-dialog-zone-detail";
import { HomeAssistant } from "../../../types";
import { ZoneMutableParams } from "../../../data/zone";
import { addDistanceToCoord } from "../../../common/location/add_distance_to_coord";
class DialogZoneDetail extends LitElement {
@property() public hass!: HomeAssistant;
@ -42,10 +43,15 @@ class DialogZoneDetail extends LitElement {
this._passive = this._params.entry.passive || false;
this._radius = this._params.entry.radius || 100;
} else {
const movedHomeLocation = addDistanceToCoord(
[this.hass.config.latitude, this.hass.config.longitude],
500,
500
);
this._name = "";
this._icon = "";
this._latitude = this.hass.config.latitude;
this._longitude = this.hass.config.longitude;
this._latitude = movedHomeLocation[0];
this._longitude = movedHomeLocation[1];
this._passive = false;
this._radius = 100;
}
@ -57,7 +63,7 @@ class DialogZoneDetail extends LitElement {
return html``;
}
return html`
<mwc-dialog
<ha-dialog
open
@closing="${this._close}"
.title=${this._params.entry
@ -99,33 +105,35 @@ class DialogZoneDetail extends LitElement {
class="flex"
.location=${this._locationValue}
.radius=${this._radius}
.icon=${this._icon || "hass:home"}
.icon=${this._icon}
@change=${this._locationChanged}
></ha-location-editor>
<paper-input
.value=${this._latitude}
.configValue=${"latitude"}
@value-changed=${this._valueChanged}
.label="${this.hass!.localize(
"ui.panel.config.zone.detail.latitude"
)}"
.errorMessage="${this.hass!.localize(
"ui.panel.config.zone.detail.required_error_msg"
)}"
.invalid=${String(this._latitude) === ""}
></paper-input>
<paper-input
.value=${this._longitude}
.configValue=${"longitude"}
@value-changed=${this._valueChanged}
.label="${this.hass!.localize(
"ui.panel.config.zone.detail.longitude"
)}"
.errorMessage="${this.hass!.localize(
"ui.panel.config.zone.detail.required_error_msg"
)}"
.invalid=${String(this._longitude) === ""}
></paper-input>
<div class="location">
<paper-input
.value=${this._latitude}
.configValue=${"latitude"}
@value-changed=${this._valueChanged}
.label="${this.hass!.localize(
"ui.panel.config.zone.detail.latitude"
)}"
.errorMessage="${this.hass!.localize(
"ui.panel.config.zone.detail.required_error_msg"
)}"
.invalid=${String(this._latitude) === ""}
></paper-input>
<paper-input
.value=${this._longitude}
.configValue=${"longitude"}
@value-changed=${this._valueChanged}
.label="${this.hass!.localize(
"ui.panel.config.zone.detail.longitude"
)}"
.errorMessage="${this.hass!.localize(
"ui.panel.config.zone.detail.required_error_msg"
)}"
.invalid=${String(this._longitude) === ""}
></paper-input>
</div>
<paper-input
.value=${this._radius}
.configValue=${"radius"}
@ -169,7 +177,7 @@ class DialogZoneDetail extends LitElement {
? this.hass!.localize("ui.panel.config.zone.detail.update")
: this.hass!.localize("ui.panel.config.zone.detail.create")}
</mwc-button>
</mwc-dialog>
</ha-dialog>
`;
}
@ -235,17 +243,34 @@ class DialogZoneDetail extends LitElement {
static get styles(): CSSResult[] {
return [
css`
mwc-dialog {
ha-dialog {
--mdc-dialog-title-ink-color: var(--primary-text-color);
--justify-action-buttons: space-between;
}
@media only screen and (min-width: 600px) {
mwc-dialog {
ha-dialog {
--mdc-dialog-min-width: 600px;
}
}
.form {
padding-bottom: 24px;
}
.location {
display: flex;
}
.location > * {
flex-grow: 1;
min-width: 0;
}
.location > *:first-child {
margin-right: 4px;
}
.location > *:last-child {
margin-left: 4px;
}
ha-location-editor {
margin-top: 16px;
}
ha-user-picker {
margin-top: 16px;
}

View File

@ -7,10 +7,12 @@ import {
property,
customElement,
query,
PropertyValues,
} from "lit-element";
import "@polymer/paper-listbox/paper-listbox";
import "@polymer/paper-item/paper-icon-item";
import "@polymer/paper-item/paper-item-body";
import "@polymer/paper-tooltip/paper-tooltip";
import "../../../components/map/ha-locations-editor";
@ -31,75 +33,153 @@ import {
ZoneMutableParams,
} from "../../../data/zone";
// tslint:disable-next-line
import { HaLocationsEditor } from "../../../components/map/ha-locations-editor";
import {
HaLocationsEditor,
MarkerLocation,
} from "../../../components/map/ha-locations-editor";
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import memoizeOne from "memoize-one";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { subscribeEntityRegistry } from "../../../data/entity_registry";
@customElement("ha-config-zone")
export class HaConfigZone extends LitElement {
@property() public hass?: HomeAssistant;
export class HaConfigZone extends SubscribeMixin(LitElement) {
@property() public hass!: HomeAssistant;
@property() public isWide?: boolean;
@property() public narrow?: boolean;
@property() private _storageItems?: Zone[];
@property() private _stateItems?: HassEntity[];
@property() private _activeEntry: string = "";
@query("ha-locations-editor") private _map?: HaLocationsEditor;
private _regEntities: string[] = [];
private _getZones = memoizeOne(
(storageItems: Zone[], stateItems: HassEntity[]): MarkerLocation[] => {
const stateLocations: MarkerLocation[] = stateItems.map((state) => {
return {
id: state.entity_id,
icon: state.attributes.icon,
name: state.attributes.friendly_name || state.entity_id,
latitude: state.attributes.latitude,
longitude: state.attributes.longitude,
radius: state.attributes.radius,
radius_color:
state.entity_id === "zone.home"
? "#03a9f4"
: state.attributes.passive
? "#9b9b9b"
: "#FF9800",
editable: false,
};
});
const storageLocations: MarkerLocation[] = storageItems.map((zone) => {
return {
...zone,
radius_color: zone.passive ? "#9b9b9b" : "#FF9800",
editable: true,
};
});
return storageLocations.concat(stateLocations);
}
);
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeEntityRegistry(this.hass.connection!, (entities) => {
this._regEntities = entities.map(
(registryEntry) => registryEntry.entity_id
);
this._filterStates();
}),
];
}
protected render(): TemplateResult | void {
if (!this.hass || this._storageItems === undefined) {
if (
!this.hass ||
this._storageItems === undefined ||
this._stateItems === undefined
) {
return html`
<hass-loading-screen></hass-loading-screen>
`;
}
const hass = this.hass;
const listBox = html`
<paper-listbox
attr-for-selected="data-id"
.selected=${this._activeEntry || ""}
>
${this._storageItems.map((entry) => {
return html`
<paper-icon-item data-id=${entry.id} @click=${
this._itemClicked
} .entry=${entry}>
<ha-icon
.icon=${entry.icon}
slot="item-icon"
>
</ha-icon>
<paper-item-body>
${entry.name}
</paper-item-body>
${
!this.narrow
? html`
<paper-icon-button
icon="hass:information-outline"
.entry=${entry}
@click=${this._openEditEntry}
></paper-icon-button>
`
: ""
}
</ha-icon>
</paper-icon-item>
`;
})}
</paper-listbox>
${this._storageItems.length === 0
const listBox =
this._storageItems.length === 0 && this._stateItems.length === 0
? html`
<div class="empty">
${hass.localize("ui.panel.config.zone.no_zones_created_yet")}
<br />
<mwc-button @click=${this._createZone}>
${hass.localize("ui.panel.config.zone.create_zone")}</mwc-button
>
</div>
`
: html``}
`;
: html`
<paper-listbox
attr-for-selected="data-id"
.selected=${this._activeEntry || ""}
>
${this._storageItems.map((entry) => {
return html`
<paper-icon-item
data-id=${entry.id}
@click=${this._itemClicked}
.entry=${entry}
>
<ha-icon .icon=${entry.icon} slot="item-icon"> </ha-icon>
<paper-item-body>
${entry.name}
</paper-item-body>
${!this.narrow
? html`
<paper-icon-button
icon="hass:pencil"
.entry=${entry}
@click=${this._openEditEntry}
></paper-icon-button>
`
: ""}
</paper-icon-item>
`;
})}
${this._stateItems.map((state) => {
return html`
<paper-icon-item
data-id=${state.entity_id}
@click=${this._stateItemClicked}
>
<ha-icon .icon=${state.attributes.icon} slot="item-icon">
</ha-icon>
<paper-item-body>
${state.attributes.friendly_name || state.entity_id}
</paper-item-body>
<div style="display:inline-block">
<paper-icon-button
.entityId=${state.entity_id}
icon="hass:pencil"
disabled
></paper-icon-button>
<paper-tooltip position="left">
${state.entity_id === "zone.home"
? this.hass.localize(
"ui.panel.config.zone.edit_home_zone"
)
: this.hass.localize(
"ui.panel.config.zone.configured_in_yaml"
)}
</paper-tooltip>
</div>
</paper-icon-item>
`;
})}
</paper-listbox>
`;
return html`
<hass-subpage
.header=${hass.localize("ui.panel.config.zone.caption")}
.showBackButton=${!this.isWide}
>
<hass-subpage .header=${hass.localize("ui.panel.config.zone.caption")}>
${this.narrow
? html`
<ha-config-section .isWide=${this.isWide}>
@ -114,7 +194,10 @@ export class HaConfigZone extends LitElement {
? html`
<div class="flex">
<ha-locations-editor
.locations=${this._storageItems}
.locations=${this._getZones(
this._storageItems,
this._stateItems
)}
@location-updated=${this._locationUpdated}
@radius-updated=${this._radiusUpdated}
@marker-clicked=${this._markerClicked}
@ -134,15 +217,56 @@ export class HaConfigZone extends LitElement {
`;
}
protected firstUpdated(changedProps) {
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
this._fetchData();
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (oldHass && this._stateItems) {
this._getStates(oldHass);
}
}
private async _fetchData() {
this._storageItems = (await fetchZones(this.hass!)).sort((ent1, ent2) =>
compare(ent1.name, ent2.name)
);
this._getStates();
}
private _getStates(oldHass?: HomeAssistant) {
let changed = false;
const tempStates = Object.values(this.hass!.states).filter((entity) => {
if (computeStateDomain(entity) !== "zone") {
return false;
}
if (oldHass?.states[entity.entity_id] !== entity) {
changed = true;
}
if (this._regEntities.includes(entity.entity_id)) {
return false;
}
return true;
});
if (changed) {
this._stateItems = tempStates;
}
}
private _filterStates() {
if (!this._stateItems) {
return;
}
const tempStates = this._stateItems.filter(
(entity) => !this._regEntities.includes(entity.entity_id)
);
if (tempStates.length !== this._stateItems.length) {
this._stateItems = tempStates;
}
}
private _locationUpdated(ev: CustomEvent) {
@ -181,9 +305,19 @@ export class HaConfigZone extends LitElement {
this._openEditEntry(ev);
return;
}
const entry: Zone = (ev.currentTarget! as any).entry;
this._map?.fitMarker(entry.id);
this._zoomZone(entry.id);
}
private _stateItemClicked(ev: MouseEvent) {
const entityId = (ev.currentTarget! as HTMLElement).getAttribute(
"data-id"
)!;
this._zoomZone(entityId);
}
private _zoomZone(id: string) {
this._map?.fitMarker(id);
}
private _openEditEntry(ev: MouseEvent) {
@ -196,13 +330,29 @@ export class HaConfigZone extends LitElement {
this._storageItems = this._storageItems!.concat(
created
).sort((ent1, ent2) => compare(ent1.name, ent2.name));
if (this.narrow) {
return;
}
await this.updateComplete;
this._activeEntry = created.id;
this._map?.fitMarker(created.id);
}
private async _updateEntry(entry: Zone, values: Partial<ZoneMutableParams>) {
private async _updateEntry(
entry: Zone,
values: Partial<ZoneMutableParams>,
fitMap: boolean = false
) {
const updated = await updateZone(this.hass!, entry!.id, values);
this._storageItems = this._storageItems!.map((ent) =>
ent === entry ? updated : ent
);
if (this.narrow || !fitMap) {
return;
}
await this.updateComplete;
this._activeEntry = entry.id;
this._map?.fitMarker(entry.id);
}
private async _removeEntry(entry: Zone) {
@ -217,6 +367,9 @@ ${this.hass!.localize("ui.panel.config.zone.confirm_delete2")}`)
try {
await deleteZone(this.hass!, entry!.id);
this._storageItems = this._storageItems!.filter((ent) => ent !== entry);
if (!this.narrow) {
this._map?.fitMap();
}
return true;
} catch (err) {
return false;
@ -228,7 +381,7 @@ ${this.hass!.localize("ui.panel.config.zone.confirm_delete2")}`)
entry,
createEntry: (values) => this._createEntry(values),
updateEntry: entry
? (values) => this._updateEntry(entry, values)
? (values) => this._updateEntry(entry, values, true)
: undefined,
removeEntry: entry ? () => this._removeEntry(entry) : undefined,
});
@ -244,6 +397,10 @@ ${this.hass!.localize("ui.panel.config.zone.confirm_delete2")}`)
margin: 16px auto;
overflow: hidden;
}
ha-icon,
paper-icon-button:not([disabled]) {
color: var(--secondary-text-color);
}
.empty {
text-align: center;
padding: 8px;
@ -256,7 +413,8 @@ ${this.hass!.localize("ui.panel.config.zone.confirm_delete2")}`)
flex-grow: 1;
height: 100%;
}
.flex paper-listbox {
.flex paper-listbox,
.flex .empty {
border-left: 1px solid var(--divider-color);
width: 250px;
}

View File

@ -107,7 +107,6 @@ class HaPanelMap extends LocalizeMixin(PolymerElement) {
Object.keys(hass.states).forEach((entityId) => {
var entity = hass.states[entityId];
var title = computeStateName(entity);
if (
(entity.attributes.hidden && computeStateDomain(entity) !== "zone") ||
@ -118,6 +117,7 @@ class HaPanelMap extends LocalizeMixin(PolymerElement) {
return;
}
var title = computeStateName(entity);
var icon;
if (computeStateDomain(entity) === "zone") {

View File

@ -1360,6 +1360,8 @@
"create_zone": "Create Zone",
"add_zone": "Add Zone",
"confirm_delete": "Are you sure you want to delete this zone?",
"configured_in_yaml": "Zones configured via configuration.yaml cannot be edited via the UI.",
"edit_home_zone": "The location of your home can be changed in the gerenal config.",
"detail": {
"new_zone": "New Zone",
"name": "Name",