mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 17:26:42 +00:00
Sort all elements on the area page (#11338)
This commit is contained in:
parent
7d335d7d85
commit
a4ae1bee79
@ -1,5 +1,6 @@
|
||||
import { Connection, createCollection } from "home-assistant-js-websocket";
|
||||
import { computeStateName } from "../common/entity/compute_state_name";
|
||||
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import { HomeAssistant } from "../types";
|
||||
import { EntityRegistryEntry } from "./entity_registry";
|
||||
@ -99,3 +100,8 @@ export const subscribeDeviceRegistry = (
|
||||
conn,
|
||||
onChange
|
||||
);
|
||||
|
||||
export const sortDeviceRegistryByName = (entries: DeviceRegistryEntry[]) =>
|
||||
entries.sort((entry1, entry2) =>
|
||||
caseInsensitiveStringCompare(entry1.name || "", entry2.name || "")
|
||||
);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Connection, createCollection } from "home-assistant-js-websocket";
|
||||
import { Store } from "home-assistant-js-websocket/dist/store";
|
||||
import { computeStateName } from "../common/entity/compute_state_name";
|
||||
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import { HomeAssistant } from "../types";
|
||||
|
||||
@ -133,3 +134,8 @@ export const subscribeEntityRegistry = (
|
||||
conn,
|
||||
onChange
|
||||
);
|
||||
|
||||
export const sortEntityRegistryByName = (entries: EntityRegistryEntry[]) =>
|
||||
entries.sort((entry1, entry2) =>
|
||||
caseInsensitiveStringCompare(entry1.name || "", entry2.name || "")
|
||||
);
|
||||
|
@ -1,13 +1,17 @@
|
||||
import "@material/mwc-button";
|
||||
import { mdiImagePlus, mdiPencil } from "@mdi/js";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import { mdiImagePlus, mdiPencil } from "@mdi/js";
|
||||
import { HassEntity } from "home-assistant-js-websocket/dist/types";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
|
||||
import { groupBy } from "../../../common/util/group-by";
|
||||
import { afterNextRender } from "../../../common/util/render-status";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-button";
|
||||
@ -17,14 +21,19 @@ import {
|
||||
deleteAreaRegistryEntry,
|
||||
updateAreaRegistryEntry,
|
||||
} from "../../../data/area_registry";
|
||||
import { AutomationEntity } from "../../../data/automation";
|
||||
import {
|
||||
computeDeviceName,
|
||||
DeviceRegistryEntry,
|
||||
sortDeviceRegistryByName,
|
||||
} from "../../../data/device_registry";
|
||||
import {
|
||||
computeEntityRegistryName,
|
||||
EntityRegistryEntry,
|
||||
sortEntityRegistryByName,
|
||||
} from "../../../data/entity_registry";
|
||||
import { SceneEntity } from "../../../data/scene";
|
||||
import { ScriptEntity } from "../../../data/script";
|
||||
import { findRelated, RelatedResult } from "../../../data/search";
|
||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
@ -35,11 +44,11 @@ import {
|
||||
loadAreaRegistryDetailDialog,
|
||||
showAreaRegistryDetailDialog,
|
||||
} from "./show-dialog-area-registry-detail";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { SceneEntity } from "../../../data/scene";
|
||||
import { ScriptEntity } from "../../../data/script";
|
||||
import { AutomationEntity } from "../../../data/automation";
|
||||
import { groupBy } from "../../../common/util/group-by";
|
||||
|
||||
declare type NameAndEntity<EntityType extends HassEntity> = {
|
||||
name: string;
|
||||
entity: EntityType;
|
||||
};
|
||||
|
||||
@customElement("ha-config-area-page")
|
||||
class HaConfigAreaPage extends LitElement {
|
||||
@ -136,10 +145,59 @@ class HaConfigAreaPage extends LitElement {
|
||||
this.entities
|
||||
);
|
||||
|
||||
const grouped = groupBy(entities, (entity) =>
|
||||
// Pre-compute the entity and device names, so we can sort by them
|
||||
if (devices) {
|
||||
devices.forEach((entry) => {
|
||||
entry.name = computeDeviceName(entry, this.hass);
|
||||
});
|
||||
sortDeviceRegistryByName(devices);
|
||||
}
|
||||
if (entities) {
|
||||
entities.forEach((entry) => {
|
||||
entry.name = computeEntityRegistryName(this.hass, entry);
|
||||
});
|
||||
sortEntityRegistryByName(entities);
|
||||
}
|
||||
|
||||
// Group entities by domain
|
||||
const groupedEntities = groupBy(entities, (entity) =>
|
||||
computeDomain(entity.entity_id)
|
||||
);
|
||||
|
||||
// Pre-compute the name also for the grouped and related entities so we can sort by them
|
||||
let groupedAutomations: NameAndEntity<AutomationEntity>[] = [];
|
||||
let groupedScenes: NameAndEntity<SceneEntity>[] = [];
|
||||
let groupedScripts: NameAndEntity<ScriptEntity>[] = [];
|
||||
let relatedAutomations: NameAndEntity<AutomationEntity>[] = [];
|
||||
let relatedScenes: NameAndEntity<SceneEntity>[] = [];
|
||||
let relatedScripts: NameAndEntity<ScriptEntity>[] = [];
|
||||
|
||||
if (isComponentLoaded(this.hass, "automation")) {
|
||||
({
|
||||
groupedEntities: groupedAutomations,
|
||||
relatedEntities: relatedAutomations,
|
||||
} = this._prepareEntities<AutomationEntity>(
|
||||
groupedEntities.automation,
|
||||
this._related?.automation
|
||||
));
|
||||
}
|
||||
|
||||
if (isComponentLoaded(this.hass, "scene")) {
|
||||
({ groupedEntities: groupedScenes, relatedEntities: relatedScenes } =
|
||||
this._prepareEntities<SceneEntity>(
|
||||
groupedEntities.scene,
|
||||
this._related?.scene
|
||||
));
|
||||
}
|
||||
|
||||
if (isComponentLoaded(this.hass, "script")) {
|
||||
({ groupedEntities: groupedScripts, relatedEntities: relatedScripts } =
|
||||
this._prepareEntities<ScriptEntity>(
|
||||
groupedEntities.script,
|
||||
this._related?.script
|
||||
));
|
||||
}
|
||||
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
.hass=${this.hass}
|
||||
@ -208,9 +266,7 @@ class HaConfigAreaPage extends LitElement {
|
||||
html`
|
||||
<a href="/config/devices/device/${device.id}">
|
||||
<paper-item>
|
||||
<paper-item-body>
|
||||
${computeDeviceName(device, this.hass)}
|
||||
</paper-item-body>
|
||||
<paper-item-body> ${device.name} </paper-item-body>
|
||||
<ha-icon-next></ha-icon-next>
|
||||
</paper-item>
|
||||
</a>
|
||||
@ -240,9 +296,7 @@ class HaConfigAreaPage extends LitElement {
|
||||
@click=${this._openEntity}
|
||||
.entity=${entity}
|
||||
>
|
||||
<paper-item-body>
|
||||
${computeEntityRegistryName(this.hass, entity)}
|
||||
</paper-item-body>
|
||||
<paper-item-body> ${entity.name} </paper-item-body>
|
||||
<ha-icon-next></ha-icon-next>
|
||||
</paper-item>
|
||||
`
|
||||
@ -264,43 +318,33 @@ class HaConfigAreaPage extends LitElement {
|
||||
"ui.panel.config.devices.automation.automations"
|
||||
)}
|
||||
>
|
||||
${grouped.automation?.length
|
||||
${groupedAutomations?.length
|
||||
? html`<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.areas.assigned_to_area"
|
||||
)}:
|
||||
</h3>
|
||||
${grouped.automation.map((entity) => {
|
||||
const entityState = this.hass.states[
|
||||
entity.entity_id
|
||||
] as AutomationEntity | undefined;
|
||||
return entityState
|
||||
? this._renderAutomation(entityState)
|
||||
: "";
|
||||
})}`
|
||||
${groupedAutomations.map((automation) =>
|
||||
this._renderAutomation(
|
||||
automation.name,
|
||||
automation.entity
|
||||
)
|
||||
)}`
|
||||
: ""}
|
||||
${this._related?.automation?.filter(
|
||||
(entityId) =>
|
||||
!grouped.automation?.find(
|
||||
(entity) => entity.entity_id === entityId
|
||||
)
|
||||
).length
|
||||
${relatedAutomations?.length
|
||||
? html`<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.areas.targeting_area"
|
||||
)}:
|
||||
</h3>
|
||||
${this._related.automation.map((scene) => {
|
||||
const entityState = this.hass.states[scene] as
|
||||
| AutomationEntity
|
||||
| undefined;
|
||||
return entityState
|
||||
? this._renderAutomation(entityState)
|
||||
: "";
|
||||
})}`
|
||||
${relatedAutomations.map((automation) =>
|
||||
this._renderAutomation(
|
||||
automation.name,
|
||||
automation.entity
|
||||
)
|
||||
)}`
|
||||
: ""}
|
||||
${!grouped.automation?.length &&
|
||||
!this._related?.automation?.length
|
||||
${!groupedAutomations?.length && !relatedAutomations?.length
|
||||
? html`
|
||||
<paper-item class="no-link"
|
||||
>${this.hass.localize(
|
||||
@ -321,39 +365,27 @@ class HaConfigAreaPage extends LitElement {
|
||||
"ui.panel.config.devices.scene.scenes"
|
||||
)}
|
||||
>
|
||||
${grouped.scene?.length
|
||||
${groupedScenes?.length
|
||||
? html`<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.areas.assigned_to_area"
|
||||
)}:
|
||||
</h3>
|
||||
${grouped.scene.map((entity) => {
|
||||
const entityState =
|
||||
this.hass.states[entity.entity_id];
|
||||
return entityState
|
||||
? this._renderScene(entityState)
|
||||
: "";
|
||||
})}`
|
||||
${groupedScenes.map((scene) =>
|
||||
this._renderScene(scene.name, scene.entity)
|
||||
)}`
|
||||
: ""}
|
||||
${this._related?.scene?.filter(
|
||||
(entityId) =>
|
||||
!grouped.scene?.find(
|
||||
(entity) => entity.entity_id === entityId
|
||||
)
|
||||
).length
|
||||
${relatedScenes?.length
|
||||
? html`<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.areas.targeting_area"
|
||||
)}:
|
||||
</h3>
|
||||
${this._related.scene.map((scene) => {
|
||||
const entityState = this.hass.states[scene];
|
||||
return entityState
|
||||
? this._renderScene(entityState)
|
||||
: "";
|
||||
})}`
|
||||
${relatedScenes.map((scene) =>
|
||||
this._renderScene(scene.name, scene.entity)
|
||||
)}`
|
||||
: ""}
|
||||
${!grouped.scene?.length && !this._related?.scene?.length
|
||||
${!groupedScenes?.length && !relatedScenes?.length
|
||||
? html`
|
||||
<paper-item class="no-link"
|
||||
>${this.hass.localize(
|
||||
@ -372,42 +404,27 @@ class HaConfigAreaPage extends LitElement {
|
||||
"ui.panel.config.devices.script.scripts"
|
||||
)}
|
||||
>
|
||||
${grouped.script?.length
|
||||
${groupedScripts?.length
|
||||
? html`<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.areas.assigned_to_area"
|
||||
)}:
|
||||
</h3>
|
||||
${grouped.script.map((entity) => {
|
||||
const entityState = this.hass.states[
|
||||
entity.entity_id
|
||||
] as ScriptEntity | undefined;
|
||||
return entityState
|
||||
? this._renderScript(entityState)
|
||||
: "";
|
||||
})}`
|
||||
${groupedScripts.map((script) =>
|
||||
this._renderScript(script.name, script.entity)
|
||||
)}`
|
||||
: ""}
|
||||
${this._related?.script?.filter(
|
||||
(entityId) =>
|
||||
!grouped.script?.find(
|
||||
(entity) => entity.entity_id === entityId
|
||||
)
|
||||
).length
|
||||
${relatedScripts?.length
|
||||
? html`<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.areas.targeting_area"
|
||||
)}:
|
||||
</h3>
|
||||
${this._related.script.map((scene) => {
|
||||
const entityState = this.hass.states[scene] as
|
||||
| ScriptEntity
|
||||
| undefined;
|
||||
return entityState
|
||||
? this._renderScript(entityState)
|
||||
: "";
|
||||
})}`
|
||||
${relatedScripts.map((script) =>
|
||||
this._renderScript(script.name, script.entity)
|
||||
)}`
|
||||
: ""}
|
||||
${!grouped.script?.length && !this._related?.script?.length
|
||||
${!groupedScripts?.length && !relatedScripts?.length
|
||||
? html`
|
||||
<paper-item class="no-link"
|
||||
>${this.hass.localize(
|
||||
@ -425,7 +442,51 @@ class HaConfigAreaPage extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderScene(entityState: SceneEntity) {
|
||||
private _prepareEntities<EntityType extends HassEntity>(
|
||||
entries?: EntityRegistryEntry[],
|
||||
relatedEntityIds?: string[]
|
||||
): {
|
||||
groupedEntities: NameAndEntity<EntityType>[];
|
||||
relatedEntities: NameAndEntity<EntityType>[];
|
||||
} {
|
||||
const groupedEntities: NameAndEntity<EntityType>[] = [];
|
||||
const relatedEntities: NameAndEntity<EntityType>[] = [];
|
||||
|
||||
if (entries?.length) {
|
||||
entries.forEach((entity) => {
|
||||
const entityState = this.hass.states[
|
||||
entity.entity_id
|
||||
] as unknown as EntityType;
|
||||
if (entityState) {
|
||||
groupedEntities.push({
|
||||
name: computeStateName(entityState),
|
||||
entity: entityState,
|
||||
});
|
||||
}
|
||||
});
|
||||
groupedEntities.sort((entry1, entry2) =>
|
||||
caseInsensitiveStringCompare(entry1.name!, entry2.name!)
|
||||
);
|
||||
}
|
||||
if (relatedEntityIds?.length) {
|
||||
relatedEntityIds.forEach((entity) => {
|
||||
const entityState = this.hass.states[entity] as EntityType;
|
||||
if (entityState) {
|
||||
relatedEntities.push({
|
||||
name: entityState ? computeStateName(entityState) : "",
|
||||
entity: entityState,
|
||||
});
|
||||
}
|
||||
});
|
||||
relatedEntities.sort((entry1, entry2) =>
|
||||
caseInsensitiveStringCompare(entry1.name!, entry2.name!)
|
||||
);
|
||||
}
|
||||
|
||||
return { groupedEntities, relatedEntities };
|
||||
}
|
||||
|
||||
private _renderScene(name: string, entityState: SceneEntity) {
|
||||
return html`<div>
|
||||
<a
|
||||
href=${ifDefined(
|
||||
@ -435,7 +496,7 @@ class HaConfigAreaPage extends LitElement {
|
||||
)}
|
||||
>
|
||||
<paper-item .disabled=${!entityState.attributes.id}>
|
||||
<paper-item-body> ${computeStateName(entityState)} </paper-item-body>
|
||||
<paper-item-body> ${name} </paper-item-body>
|
||||
<ha-icon-next></ha-icon-next>
|
||||
</paper-item>
|
||||
</a>
|
||||
@ -449,7 +510,7 @@ class HaConfigAreaPage extends LitElement {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private _renderAutomation(entityState: AutomationEntity) {
|
||||
private _renderAutomation(name: string, entityState: AutomationEntity) {
|
||||
return html`<div>
|
||||
<a
|
||||
href=${ifDefined(
|
||||
@ -459,7 +520,7 @@ class HaConfigAreaPage extends LitElement {
|
||||
)}
|
||||
>
|
||||
<paper-item .disabled=${!entityState.attributes.id}>
|
||||
<paper-item-body> ${computeStateName(entityState)} </paper-item-body>
|
||||
<paper-item-body> ${name} </paper-item-body>
|
||||
<ha-icon-next></ha-icon-next>
|
||||
</paper-item>
|
||||
</a>
|
||||
@ -473,10 +534,10 @@ class HaConfigAreaPage extends LitElement {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private _renderScript(entityState: ScriptEntity) {
|
||||
private _renderScript(name: string, entityState: ScriptEntity) {
|
||||
return html`<a href=${`/config/script/edit/${entityState.entity_id}`}>
|
||||
<paper-item>
|
||||
<paper-item-body> ${computeStateName(entityState)} </paper-item-body>
|
||||
<paper-item-body> ${name} </paper-item-body>
|
||||
<ha-icon-next></ha-icon-next>
|
||||
</paper-item>
|
||||
</a>`;
|
||||
|
Loading…
x
Reference in New Issue
Block a user