Add default icons for area and floors (#20214)

* Add default icons for area and floors

* Add default icon for floor based on level

* Allow deleting floor level

* Use texture box for area

---------

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
This commit is contained in:
Bram Kragten 2024-03-28 16:35:56 +01:00 committed by GitHub
parent 6b8f4e92a7
commit d3e62454a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 146 additions and 61 deletions

View File

@ -1,12 +1,12 @@
import { mdiSofa } from "@mdi/js";
import { mdiTextureBox } from "@mdi/js";
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import { showAreaFilterDialog } from "../dialogs/area-filter/show-area-filter-dialog";
import { HomeAssistant } from "../types";
import "./ha-icon-next";
import "./ha-svg-icon";
import "./ha-textfield";
import "./ha-icon-next";
export type AreaFilterValue = {
hidden?: string[];
@ -51,7 +51,7 @@ export class HaAreaPicker extends LitElement {
@keydown=${this._edit}
.disabled=${this.disabled}
>
<ha-svg-icon slot="graphic" .path=${mdiSofa}></ha-svg-icon>
<ha-svg-icon slot="graphic" .path=${mdiTextureBox}></ha-svg-icon>
<span>${this.label}</span>
<span slot="secondary">${description}</span>
<ha-icon-next

View File

@ -1,10 +1,12 @@
import { mdiTextureBox } from "@mdi/js";
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { LitElement, PropertyValues, TemplateResult, html, nothing } from "lit";
import { LitElement, PropertyValues, TemplateResult, html } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event";
import { computeDomain } from "../common/entity/compute_domain";
import { stringCompare } from "../common/string/compare";
import {
ScorableTextItem,
fuzzyFilterSort,
@ -26,10 +28,10 @@ import { HomeAssistant, ValueChangedEvent } from "../types";
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
import "./ha-combo-box";
import type { HaComboBox } from "./ha-combo-box";
import "./ha-floor-icon";
import "./ha-icon-button";
import "./ha-list-item";
import "./ha-svg-icon";
import { stringCompare } from "../common/string/compare";
type ScorableAreaFloorEntry = ScorableTextItem & FloorAreaEntry;
@ -40,6 +42,7 @@ interface FloorAreaEntry {
strings: string[];
type: "floor" | "area";
hasFloor?: boolean;
level: number | null;
}
const rowRenderer: ComboBoxLitRenderer<FloorAreaEntry> = (item) =>
@ -49,9 +52,14 @@ const rowRenderer: ComboBoxLitRenderer<FloorAreaEntry> = (item) =>
? "--mdc-list-side-padding-left: 48px;"
: ""}
>
${item.icon
? html`<ha-icon slot="graphic" .icon=${item.icon}></ha-icon>`
: nothing}
${item.type === "floor"
? html`<ha-floor-icon slot="graphic" .floor=${item}></ha-floor-icon>`
: item.icon
? html`<ha-icon slot="graphic" .icon=${item.icon}></ha-icon>`
: html`<ha-svg-icon
slot="graphic"
.path=${mdiTextureBox}
></ha-svg-icon>`}
${item.name}
</ha-list-item>`;
@ -165,6 +173,7 @@ export class HaAreaFloorPicker extends SubscribeMixin(LitElement) {
name: this.hass.localize("ui.components.area-picker.no_areas"),
icon: null,
strings: [],
level: null,
},
];
}
@ -316,6 +325,7 @@ export class HaAreaFloorPicker extends SubscribeMixin(LitElement) {
name: this.hass.localize("ui.components.area-picker.no_match"),
icon: null,
strings: [],
level: null,
},
];
}
@ -350,6 +360,7 @@ export class HaAreaFloorPicker extends SubscribeMixin(LitElement) {
name: floor.name,
icon: floor.icon,
strings: [floor.floor_id, ...floor.aliases, floor.name],
level: floor.level,
});
}
output.push(
@ -360,6 +371,7 @@ export class HaAreaFloorPicker extends SubscribeMixin(LitElement) {
icon: area.icon,
strings: [area.area_id, ...area.aliases, area.name],
hasFloor: true,
level: null,
}))
);
});
@ -373,6 +385,7 @@ export class HaAreaFloorPicker extends SubscribeMixin(LitElement) {
),
icon: null,
strings: [],
level: null,
});
}
@ -383,6 +396,7 @@ export class HaAreaFloorPicker extends SubscribeMixin(LitElement) {
name: area.name,
icon: area.icon,
strings: [area.area_id, ...area.aliases, area.name],
level: null,
}))
);

View File

@ -1,14 +1,15 @@
import { mdiTextureBox } from "@mdi/js";
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
import { HassEntity } from "home-assistant-js-websocket";
import { html, LitElement, nothing, PropertyValues, TemplateResult } from "lit";
import { LitElement, PropertyValues, TemplateResult, html } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event";
import { computeDomain } from "../common/entity/compute_domain";
import {
fuzzyFilterSort,
ScorableTextItem,
fuzzyFilterSort,
} from "../common/string/filter/sequence-matching";
import {
AreaRegistryEntry,
@ -41,7 +42,7 @@ const rowRenderer: ComboBoxLitRenderer<AreaRegistryEntry> = (item) =>
>
${item.icon
? html`<ha-icon slot="graphic" .icon=${item.icon}></ha-icon>`
: nothing}
: html`<ha-svg-icon slot="graphic" .path=${mdiTextureBox}></ha-svg-icon>`}
${item.name}
</ha-list-item>`;

View File

@ -1,4 +1,5 @@
import "@material/mwc-menu/mwc-menu-surface";
import { mdiTextureBox } from "@mdi/js";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
@ -15,6 +16,9 @@ import { SubscribeMixin } from "../mixins/subscribe-mixin";
import { haStyleScrollbar } from "../resources/styles";
import type { HomeAssistant } from "../types";
import "./ha-check-list-item";
import "./ha-floor-icon";
import "./ha-icon";
import "./ha-svg-icon";
@customElement("ha-filter-floor-areas")
export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
@ -70,12 +74,10 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
graphic="icon"
@request-selected=${this._handleItemClick}
>
${floor.icon
? html`<ha-icon
slot="graphic"
.icon=${floor.icon}
></ha-icon>`
: nothing}
<ha-floor-icon
slot="graphic"
.floor=${floor}
></ha-floor-icon>
${floor.name}
</ha-check-list-item>
${repeat(
@ -108,7 +110,10 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
>
${area.icon
? html`<ha-icon slot="graphic" .icon=${area.icon}></ha-icon>`
: nothing}
: html`<ha-svg-icon
slot="graphic"
.path=${mdiTextureBox}
></ha-svg-icon>`}
${area.name}
</ha-check-list-item>`;
}

View File

@ -0,0 +1,56 @@
import {
mdiHome,
mdiHomeFloor0,
mdiHomeFloor1,
mdiHomeFloor2,
mdiHomeFloor3,
mdiHomeFloorNegative1,
} from "@mdi/js";
import { LitElement, html } from "lit";
import { customElement, property } from "lit/decorators";
import { FloorRegistryEntry } from "../data/floor_registry";
import "./ha-icon";
import "./ha-svg-icon";
export const floorDefaultIconPath = (
floor: Pick<FloorRegistryEntry, "level">
) => {
switch (floor.level) {
case 0:
return mdiHomeFloor0;
case 1:
return mdiHomeFloor1;
case 2:
return mdiHomeFloor2;
case 3:
return mdiHomeFloor3;
case -1:
return mdiHomeFloorNegative1;
}
return mdiHome;
};
@customElement("ha-floor-icon")
export class HaFloorIcon extends LitElement {
@property({ attribute: false }) public floor!: Pick<
FloorRegistryEntry,
"icon" | "level"
>;
@property() public icon?: string;
protected render() {
if (this.floor.icon) {
return html`<ha-icon .icon=${this.floor.icon}></ha-icon>`;
}
const defaultPath = floorDefaultIconPath(this.floor);
return html`<ha-svg-icon .path=${defaultPath}></ha-svg-icon>`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-floor-icon": HaFloorIcon;
}
}

View File

@ -1,14 +1,14 @@
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { html, LitElement, nothing, PropertyValues, TemplateResult } from "lit";
import { LitElement, PropertyValues, TemplateResult, html } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event";
import { computeDomain } from "../common/entity/compute_domain";
import {
fuzzyFilterSort,
ScorableTextItem,
fuzzyFilterSort,
} from "../common/string/filter/sequence-matching";
import { AreaRegistryEntry } from "../data/area_registry";
import {
@ -17,24 +17,24 @@ import {
getDeviceEntityDisplayLookup,
} from "../data/device_registry";
import { EntityRegistryDisplayEntry } from "../data/entity_registry";
import {
FloorRegistryEntry,
createFloorRegistryEntry,
getFloorAreaLookup,
subscribeFloorRegistry,
} from "../data/floor_registry";
import {
showAlertDialog,
showPromptDialog,
} from "../dialogs/generic/show-dialog-box";
import { SubscribeMixin } from "../mixins/subscribe-mixin";
import { HomeAssistant, ValueChangedEvent } from "../types";
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
import "./ha-combo-box";
import type { HaComboBox } from "./ha-combo-box";
import "./ha-floor-icon";
import "./ha-icon-button";
import "./ha-list-item";
import "./ha-svg-icon";
import { SubscribeMixin } from "../mixins/subscribe-mixin";
import {
createFloorRegistryEntry,
FloorRegistryEntry,
getFloorAreaLookup,
subscribeFloorRegistry,
} from "../data/floor_registry";
type ScorableFloorRegistryEntry = ScorableTextItem & FloorRegistryEntry;
@ -43,9 +43,7 @@ const rowRenderer: ComboBoxLitRenderer<FloorRegistryEntry> = (item) =>
graphic="icon"
class=${classMap({ "add-new": item.floor_id === "add_new" })}
>
${item.icon
? html`<ha-icon slot="graphic" .icon=${item.icon}></ha-icon>`
: nothing}
<ha-floor-icon slot="graphic" .floor=${item}></ha-floor-icon>
${item.name}
</ha-list-item>`;

View File

@ -118,7 +118,7 @@ export class HaIconPicker extends LitElement {
<ha-icon .icon=${this._value || this.placeholder} slot="icon">
</ha-icon>
`
: html`<slot name="fallback"></slot>`}
: html`<slot slot="icon" name="fallback"></slot>`}
</ha-combo-box>
`;
}

View File

@ -6,10 +6,10 @@ import "@material/mwc-menu/mwc-menu-surface";
import {
mdiClose,
mdiDevices,
mdiFloorPlan,
mdiHome,
mdiLabel,
mdiPlus,
mdiSofa,
mdiTextureBox,
mdiUnfoldMoreVertical,
} from "@mdi/js";
import { ComboBoxLightOpenedChangedEvent } from "@vaadin/combo-box/vaadin-combo-box-light";
@ -18,30 +18,23 @@ import {
HassServiceTarget,
UnsubscribeFunc,
} from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, nothing, unsafeCSS } from "lit";
import { CSSResultGroup, LitElement, css, html, nothing, unsafeCSS } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ensureArray } from "../common/array/ensure-array";
import { computeCssColor } from "../common/color/compute-color";
import { hex2rgb } from "../common/color/convert-color";
import { fireEvent } from "../common/dom/fire_event";
import { stopPropagation } from "../common/dom/stop_propagation";
import { computeDomain } from "../common/entity/compute_domain";
import { computeStateName } from "../common/entity/compute_state_name";
import { isValidEntityId } from "../common/entity/valid_entity_id";
import { AreaRegistryEntry } from "../data/area_registry";
import {
computeDeviceName,
DeviceRegistryEntry,
computeDeviceName,
} from "../data/device_registry";
import { EntityRegistryDisplayEntry } from "../data/entity_registry";
import { HomeAssistant } from "../types";
import "./device/ha-device-picker";
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
import "./entity/ha-entity-picker";
import type { HaEntityPickerEntityFilterFunc } from "./entity/ha-entity-picker";
import "./ha-area-floor-picker";
import "./ha-icon-button";
import "./ha-input-helper-text";
import "./ha-svg-icon";
import { SubscribeMixin } from "../mixins/subscribe-mixin";
import {
FloorRegistryEntry,
subscribeFloorRegistry,
@ -50,9 +43,17 @@ import {
LabelRegistryEntry,
subscribeLabelRegistry,
} from "../data/label_registry";
import { computeCssColor } from "../common/color/compute-color";
import { AreaRegistryEntry } from "../data/area_registry";
import { hex2rgb } from "../common/color/convert-color";
import { SubscribeMixin } from "../mixins/subscribe-mixin";
import { HomeAssistant } from "../types";
import "./device/ha-device-picker";
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
import "./entity/ha-entity-picker";
import type { HaEntityPickerEntityFilterFunc } from "./entity/ha-entity-picker";
import "./ha-area-floor-picker";
import { floorDefaultIconPath } from "./ha-floor-icon";
import "./ha-icon-button";
import "./ha-input-helper-text";
import "./ha-svg-icon";
@customElement("ha-target-picker")
export class HaTargetPicker extends SubscribeMixin(LitElement) {
@ -138,7 +139,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
floor?.name || floor_id,
undefined,
floor?.icon,
mdiFloorPlan
floor ? floorDefaultIconPath(floor) : mdiHome
);
})
: ""}
@ -151,7 +152,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
area?.name || area_id,
undefined,
area?.icon,
mdiSofa
mdiTextureBox
);
})
: nothing}

View File

@ -1,16 +1,16 @@
import { Connection, createCollection } from "home-assistant-js-websocket";
import { Store } from "home-assistant-js-websocket/dist/store";
import { stringCompare } from "../common/string/compare";
import { debounce } from "../common/util/debounce";
import { HomeAssistant } from "../types";
import { AreaRegistryEntry } from "./area_registry";
import { debounce } from "../common/util/debounce";
export { subscribeAreaRegistry } from "./ws-area_registry";
export interface FloorRegistryEntry {
floor_id: string;
name: string;
level: number;
level: number | null;
icon: string | null;
aliases: string[];
}

View File

@ -6,10 +6,11 @@ import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-alert";
import "../../../components/ha-aliases-editor";
import { createCloseHeading } from "../../../components/ha-dialog";
import "../../../components/ha-icon-picker";
import "../../../components/ha-picture-upload";
import "../../../components/ha-settings-row";
import "../../../components/ha-svg-icon";
import "../../../components/ha-textfield";
import "../../../components/ha-icon-picker";
import { FloorRegistryEntryMutableParams } from "../../../data/floor_registry";
import { haStyleDialog } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
@ -56,6 +57,7 @@ class DialogFloorDetail extends LitElement {
}
const entry = this._params.entry;
const nameInvalid = !this._isNameValid();
return html`
<ha-dialog
open
@ -110,7 +112,16 @@ class DialogFloorDetail extends LitElement {
.value=${this._icon}
@value-changed=${this._iconChanged}
.label=${this.hass.localize("ui.panel.config.areas.editor.icon")}
></ha-icon-picker>
>
${!this._icon
? html`
<ha-floor-icon
slot="fallback"
.floor=${{ level: this._level }}
></ha-floor-icon>
`
: nothing}
</ha-icon-picker>
<h3 class="header">
${this.hass.localize(
@ -157,7 +168,7 @@ class DialogFloorDetail extends LitElement {
private _levelChanged(ev) {
this._error = undefined;
this._level = Number(ev.target.value);
this._level = ev.target.value === "" ? null : Number(ev.target.value);
}
private _iconChanged(ev) {

View File

@ -1,3 +1,4 @@
import { ActionDetail } from "@material/mwc-list";
import {
mdiDelete,
mdiDotsVertical,
@ -17,9 +18,9 @@ import {
import { customElement, property, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
import { ActionDetail } from "@material/mwc-list";
import { formatListWithAnds } from "../../../common/string/format-list";
import "../../../components/ha-fab";
import "../../../components/ha-floor-icon";
import "../../../components/ha-icon-button";
import "../../../components/ha-svg-icon";
import {
@ -39,6 +40,7 @@ import {
showConfirmationDialog,
} from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/hass-tabs-subpage";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { HomeAssistant, Route } from "../../../types";
import "../ha-config-section";
import { configSections } from "../ha-panel-config";
@ -47,7 +49,6 @@ import {
showAreaRegistryDetailDialog,
} from "./show-dialog-area-registry-detail";
import { showFloorRegistryDetailDialog } from "./show-dialog-floor-registry-detail";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
@customElement("ha-config-areas-dashboard")
export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
@ -154,9 +155,7 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
html`<div class="floor">
<div class="header">
<h2>
${floor.icon
? html`<ha-icon .icon=${floor.icon}></ha-icon>`
: nothing}
<ha-floor-icon .floor=${floor}></ha-floor-icon>
${floor.name}
</h2>
<ha-button-menu