1
0
mirror of https://github.com/home-assistant/frontend.git synced 2026-04-02 00:27:49 +01:00

Support more-info-view query param (#30282)

* Support more-info-view query param

* Remove unintended subview logic and add details view

* Reset childView
This commit is contained in:
Aidan Timson
2026-03-24 09:26:08 +00:00
committed by GitHub
parent a8070b322c
commit 5bbfa36228
3 changed files with 108 additions and 73 deletions

View File

@@ -7,6 +7,22 @@ import { CONTINUOUS_DOMAINS } from "../../data/logbook";
import type { HomeAssistant } from "../../types";
import { isNumericEntity } from "../../data/history";
export const MORE_INFO_VIEWS = [
"info",
"history",
"settings",
"related",
"add_to",
"details",
] as const;
export type MoreInfoView = (typeof MORE_INFO_VIEWS)[number];
export const isMoreInfoView = (
value: string | undefined
): value is MoreInfoView =>
value !== undefined && (MORE_INFO_VIEWS as readonly string[]).includes(value);
export const DOMAINS_NO_INFO = ["camera", "configurator"];
/**
* Entity domains that should be editable *if* they have an id present;

View File

@@ -22,7 +22,7 @@ import { customElement, property, query, state } from "lit/decorators";
import { cache } from "lit/directives/cache";
import { classMap } from "lit/directives/class-map";
import { keyed } from "lit/directives/keyed";
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
import type { HASSDomEvent } from "../../common/dom/fire_event";
import { fireEvent } from "../../common/dom/fire_event";
import { stopPropagation } from "../../common/dom/stop_propagation";
import { computeAreaName } from "../../common/entity/compute_area_name";
@@ -72,6 +72,7 @@ import {
DOMAINS_WITH_MORE_INFO,
EDITABLE_DOMAINS_WITH_ID,
EDITABLE_DOMAINS_WITH_UNIQUE_ID,
type MoreInfoView,
computeShowHistoryComponent,
computeShowLogBookComponent,
} from "./const";
@@ -79,6 +80,7 @@ import "./controls/more-info-default";
import type { FavoritesDialogContext } from "./favorites";
import { getFavoritesDialogHandler } from "./favorites";
import "./ha-more-info-add-to";
import "./ha-more-info-details";
import "./ha-more-info-history-and-logbook";
import "./ha-more-info-info";
import "./ha-more-info-settings";
@@ -86,16 +88,14 @@ import "./more-info-content";
export interface MoreInfoDialogParams {
entityId: string | null;
view?: View;
view?: MoreInfoView;
/** @deprecated Use `view` instead */
tab?: View;
tab?: MoreInfoView;
large?: boolean;
data?: Record<string, any>;
parentElement?: LitElement;
}
type View = "info" | "history" | "settings" | "related" | "add_to";
interface ChildView {
viewTag: string;
viewTitle?: string;
@@ -112,7 +112,7 @@ declare global {
}
}
const DEFAULT_VIEW: View = "info";
const DEFAULT_VIEW: MoreInfoView = "info";
@customElement("ha-more-info-dialog")
export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
@@ -134,9 +134,9 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
@state() private _data?: Record<string, any>;
@state() private _currView: View = DEFAULT_VIEW;
@state() private _currView: MoreInfoView = DEFAULT_VIEW;
@state() private _initialView: View = DEFAULT_VIEW;
@state() private _initialView: MoreInfoView = DEFAULT_VIEW;
@state() private _childView?: ChildView;
@@ -163,10 +163,15 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
return;
}
const view = params.view || params.tab || DEFAULT_VIEW;
this._data = params.data;
this._currView = params.view || DEFAULT_VIEW;
this._initialView = params.view || DEFAULT_VIEW;
this._currView = view;
this._initialView = view;
this._childView = undefined;
this._infoEditMode = false;
this._detailsYamlMode = false;
this.large = params.large ?? false;
this._fill = false;
this._open = true;
@@ -253,7 +258,7 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
return entity?.device_id ?? null;
}
private _setView(view: View) {
private _setView(view: MoreInfoView) {
history.replaceState(
{
...history.state,
@@ -278,6 +283,13 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
this._detailsYamlMode = false;
return;
}
if (
this._initialView !== DEFAULT_VIEW &&
this._currView === this._initialView
) {
this._resetInitialView();
return;
}
if (this._initialView !== this._currView) {
this._setView(this._initialView);
return;
@@ -404,7 +416,9 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
this._resetInitialView();
break;
case "details":
this._showDetails();
this._setView("details");
break;
default:
break;
}
}
@@ -449,15 +463,6 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
this._entry = result.entity_entry;
}
private _showDetails(): void {
import("./ha-more-info-details");
this._childView = {
viewTag: "ha-more-info-details",
viewTitle: this.hass.localize("ui.dialogs.more_info_control.details"),
viewParams: { entityId: this._entityId },
};
}
private async _copyFavorites() {
const favoritesContext = this._getFavoritesContext();
@@ -509,13 +514,8 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
(deviceId && this.hass.devices[deviceId].entry_type) || "device";
const isDefaultView = this._currView === DEFAULT_VIEW && !this._childView;
const isSpecificInitialView =
this._initialView !== DEFAULT_VIEW && !this._childView;
const showCloseIcon =
(isDefaultView &&
this._parentEntityIds.length === 0 &&
!this._childView) ||
(isSpecificInitialView && !this._childView);
isDefaultView && this._parentEntityIds.length === 0 && !this._childView;
const context = stateObj
? getEntityContext(
@@ -549,7 +549,11 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
const breadcrumb = [areaName, deviceName, entityName].filter(
(v): v is string => Boolean(v)
);
const title = this._childView?.viewTitle || breadcrumb.pop() || entityId;
const defaultTitle = breadcrumb.pop() || entityId;
const title =
this._currView === "details"
? this.hass.localize("ui.dialogs.more_info_control.details")
: this._childView?.viewTitle || defaultTitle;
const favoritesContext =
this._entry && stateObj
@@ -774,26 +778,16 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
`
: nothing}
`
: isSpecificInitialView
: this._currView === "details"
? html`
<ha-dropdown
<ha-icon-button
slot="headerActionItems"
@closed=${stopPropagation}
@wa-select=${this._handleMenuAction}
placement="bottom-end"
>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<ha-dropdown-item value="info">
<ha-svg-icon slot="icon" .path=${mdiInformationOutline}>
</ha-svg-icon>
${this.hass.localize("ui.dialogs.more_info_control.info")}
</ha-dropdown-item>
</ha-dropdown>
.label=${this.hass.localize(
"ui.dialogs.more_info_control.toggle_yaml_mode"
)}
.path=${mdiCodeBraces}
@click=${this._toggleDetailsYamlMode}
></ha-icon-button>
`
: this._childView?.viewTag === "ha-more-info-details"
? html`
@@ -828,12 +822,24 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
this._childView
? html`
<div class="child-view">
${dynamicElement(this._childView.viewTag, {
hass: this.hass,
entry: this._entry,
params: this._childView.viewParams,
yamlMode: this._detailsYamlMode,
})}
${this._childView.viewTag ===
"ha-more-info-view-voice-assistants"
? html`
<ha-more-info-view-voice-assistants
.hass=${this.hass}
.entry=${this._entry!}
.params=${this._childView.viewParams}
></ha-more-info-view-voice-assistants>
`
: this._childView.viewTag ===
"ha-more-info-view-vacuum-segment-mapping"
? html`
<ha-more-info-view-vacuum-segment-mapping
.hass=${this.hass}
.params=${this._childView.viewParams}
></ha-more-info-view-vacuum-segment-mapping>
`
: nothing}
</div>
`
: this._currView === "info"
@@ -879,7 +885,16 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
@add-to-action-selected=${this._goBack}
></ha-more-info-add-to>
`
: nothing
: this._currView === "details"
? html`
<ha-more-info-details
.hass=${this.hass}
.entry=${this._entry}
.params=${{ entityId }}
.yamlMode=${this._detailsYamlMode}
></ha-more-info-details>
`
: nothing
)}
</div>
`
@@ -898,14 +913,11 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
const previousChildView = changedProps.get("_childView") as
| ChildView
const previousView = changedProps.get("_currView") as
| MoreInfoView
| undefined;
if (
previousChildView?.viewTag === "ha-more-info-details" &&
this._childView?.viewTag !== "ha-more-info-details"
) {
if (previousView === "details" && this._currView !== "details") {
const dialog =
this._dialogElement?.shadowRoot?.querySelector("ha-dialog");
if (dialog) {
@@ -914,7 +926,6 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
}
if (changedProps.has("_currView")) {
this._childView = undefined;
this._infoEditMode = false;
this._detailsYamlMode = false;
}
@@ -935,15 +946,25 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
window.addEventListener("show-dialog", this._disableEscapeKeyClose);
}
private _handleMoreInfoEvent(ev) {
private _handleMoreInfoEvent(ev: HASSDomEvent<MoreInfoDialogParams>) {
ev.stopPropagation();
const entityId = ev.detail.entityId;
if (!entityId) {
return;
}
const view = ev.detail.view || ev.detail.tab || DEFAULT_VIEW;
if (entityId === this._entityId) {
this._infoEditMode = false;
this._detailsYamlMode = false;
this._setView(view);
return;
}
this._parentEntityIds = [...this._parentEntityIds, this._entityId!];
this._entityId = entityId;
this._currView = DEFAULT_VIEW;
this._currView = view === "details" ? view : DEFAULT_VIEW;
this._initialView = view;
this._infoEditMode = false;
this._detailsYamlMode = false;
this._childView = undefined;
this._loadEntityRegistryEntry();
}
@@ -998,12 +1019,6 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
);
}
.child-view {
display: flex;
flex-direction: column;
flex: 1;
}
ha-more-info-history-and-logbook {
padding: var(--ha-space-2) var(--ha-space-6) var(--ha-space-6)
var(--ha-space-6);

View File

@@ -70,6 +70,7 @@ import {
showAlertDialog,
showConfirmationDialog,
} from "../../dialogs/generic/show-dialog-box";
import { isMoreInfoView } from "../../dialogs/more-info/const";
import { showMoreInfoDialog } from "../../dialogs/more-info/show-ha-more-info-dialog";
import { showQuickBar } from "../../dialogs/quick-bar/show-dialog-quick-bar";
import { showVoiceCommandDialog } from "../../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
@@ -716,11 +717,18 @@ class HUIRoot extends LitElement {
this._showVoiceCommandDialog();
} else if (searchParams["more-info-entity-id"]) {
const entityId = searchParams["more-info-entity-id"];
const view = searchParams["more-info-view"];
this._clearParam("more-info-entity-id");
if (view) {
this._clearParam("more-info-view");
}
// Wait for the next render to ensure the view is fully loaded
// because the more info dialog is closed when the url changes
afterNextRender(() => {
this._showMoreInfoDialog(entityId);
showMoreInfoDialog(this, {
entityId,
view: isMoreInfoView(view) ? view : undefined,
});
});
}
}
@@ -975,10 +983,6 @@ class HUIRoot extends LitElement {
showVoiceCommandDialog(this, this.hass, { pipeline_id: "last_used" });
};
private _showMoreInfoDialog(entityId: string): void {
showMoreInfoDialog(this, { entityId });
}
private _enableEditMode = async () => {
if (this._yamlMode) {
showAlertDialog(this, {