Compact more info header (#15448)

This commit is contained in:
Paul Bottein 2023-02-20 14:58:10 +01:00 committed by GitHub
parent 9c42eb5130
commit 8754947133
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 218 additions and 150 deletions

View File

@ -17,7 +17,6 @@ import "../../../../src/components/ha-formfield";
import "../../../../src/components/ha-header-bar";
import "../../../../src/components/ha-icon-button";
import "../../../../src/components/ha-radio";
import "../../../../src/components/ha-related-items";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import {
AccessPoints,

View File

@ -36,6 +36,12 @@ export class HaHeaderBar extends LitElement {
position: static;
color: var(--mdc-theme-on-primary, #fff);
}
.mdc-top-app-bar__section.mdc-top-app-bar__section--align-start {
flex: 1;
}
.mdc-top-app-bar__section.mdc-top-app-bar__section--align-end {
flex: none;
}
`,
];
}

View File

@ -1,41 +1,54 @@
import {
mdiChartBoxOutline,
mdiClose,
mdiCogOutline,
mdiDevices,
mdiDotsVertical,
mdiInformationOutline,
mdiPencilOutline,
} from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket";
import "@material/mwc-button";
import "@material/mwc-tab";
import "@material/mwc-tab-bar";
import { mdiClose, mdiPencil } from "@mdi/js";
import { css, html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import { cache } from "lit/directives/cache";
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 { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event";
import { navigate } from "../../common/navigate";
import "../../components/ha-button-menu";
import "../../components/ha-dialog";
import "../../components/ha-header-bar";
import "../../components/ha-icon-button";
import "../../components/ha-icon-button-prev";
import "../../components/ha-list-item";
import "../../components/ha-related-items";
import { EntityRegistryEntry } from "../../data/entity_registry";
import { haStyleDialog } from "../../resources/styles";
import "../../state-summary/state-card-content";
import { HomeAssistant } from "../../types";
import {
EDITABLE_DOMAINS_WITH_ID,
EDITABLE_DOMAINS_WITH_UNIQUE_ID,
DOMAINS_WITH_MORE_INFO,
computeShowHistoryComponent,
computeShowLogBookComponent,
DOMAINS_WITH_MORE_INFO,
EDITABLE_DOMAINS_WITH_ID,
EDITABLE_DOMAINS_WITH_UNIQUE_ID,
} from "./const";
import "./controls/more-info-default";
import "./ha-more-info-history-and-logbook";
import "./ha-more-info-info";
import "./ha-more-info-settings";
import "./ha-more-info-history-and-logbook";
import "./more-info-content";
export interface MoreInfoDialogParams {
entityId: string | null;
tab?: Tab;
view?: View;
/** @deprecated Use `view` instead */
tab?: View;
}
type Tab = "info" | "history" | "settings" | "related";
type View = "info" | "history" | "settings" | "related";
@customElement("ha-more-info-dialog")
export class MoreInfoDialog extends LitElement {
@ -45,7 +58,7 @@ export class MoreInfoDialog extends LitElement {
@state() private _entityId?: string | null;
@state() private _currTab: Tab = "info";
@state() private _currView: View = "info";
public showDialog(params: MoreInfoDialogParams) {
this._entityId = params.entityId;
@ -53,7 +66,7 @@ export class MoreInfoDialog extends LitElement {
this.closeDialog();
return;
}
this._currTab = params.tab || "info";
this._currView = params.view || "info";
this.large = false;
}
@ -62,7 +75,7 @@ export class MoreInfoDialog extends LitElement {
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected shouldShowEditIcon(
private shouldShowEditIcon(
domain: string,
stateObj: HassEntity | undefined
): boolean {
@ -82,16 +95,77 @@ export class MoreInfoDialog extends LitElement {
return false;
}
private shouldShowHistory(domain: string): boolean {
return (
DOMAINS_WITH_MORE_INFO.includes(domain) &&
(computeShowHistoryComponent(this.hass, this._entityId!) ||
computeShowLogBookComponent(this.hass, this._entityId!))
);
}
private _getDeviceId(): string | null {
const entity = this.hass.entities[this._entityId!] as
| EntityRegistryEntry
| undefined;
return entity?.device_id ?? null;
}
private back() {
this._currView = "info";
}
private _goToHistory() {
this._currView = "history";
}
private _goToSettings(): void {
this._currView = "settings";
}
private _goToDevice(ev): void {
if (!shouldHandleRequestSelectedEvent(ev)) return;
const deviceId = this._getDeviceId();
if (!deviceId) return;
navigate(`/config/devices/device/${deviceId}`);
this.closeDialog();
}
private _goToEdit(ev) {
if (!shouldHandleRequestSelectedEvent(ev)) return;
const stateObj = this.hass.states[this._entityId!];
const domain = computeDomain(this._entityId!);
let idToPassThroughUrl = stateObj.entity_id;
if (EDITABLE_DOMAINS_WITH_ID.includes(domain) || domain === "person") {
idToPassThroughUrl = stateObj.attributes.id;
}
if (EDITABLE_DOMAINS_WITH_UNIQUE_ID.includes(domain)) {
idToPassThroughUrl = this.hass.entities[this._entityId!].unique_id;
}
navigate(`/config/${domain}/edit/${idToPassThroughUrl}`);
this.closeDialog();
}
private _goToRelated(ev): void {
if (!shouldHandleRequestSelectedEvent(ev)) return;
this._currView = "related";
}
protected render() {
if (!this._entityId) {
return html``;
return null;
}
const entityId = this._entityId;
const stateObj = this.hass.states[entityId];
const domain = computeDomain(entityId);
const name = (stateObj && computeStateName(stateObj)) || entityId;
const tabs = this._getTabs(entityId, this.hass.user!.is_admin);
const isAdmin = this.hass.user!.is_admin;
const deviceId = this._getDeviceId();
return html`
<ha-dialog
@ -103,14 +177,26 @@ export class MoreInfoDialog extends LitElement {
>
<div slot="heading" class="heading">
<ha-header-bar>
<ha-icon-button
slot="navigationIcon"
dialogAction="cancel"
.label=${this.hass.localize(
"ui.dialogs.more_info_control.dismiss"
)}
.path=${mdiClose}
></ha-icon-button>
${this._currView === "info"
? html`
<ha-icon-button
slot="navigationIcon"
dialogAction="cancel"
.label=${this.hass.localize(
"ui.dialogs.more_info_control.dismiss"
)}
.path=${mdiClose}
></ha-icon-button>
`
: html`
<ha-icon-button-prev
slot="navigationIcon"
@click=${this.back}
.label=${this.hass.localize(
"ui.dialogs.more_info_control.back_to_info"
)}
></ha-icon-button-prev>
`}
<div
slot="title"
class="main-title"
@ -119,57 +205,111 @@ export class MoreInfoDialog extends LitElement {
>
${name}
</div>
${this.shouldShowEditIcon(domain, stateObj)
${this._currView === "info"
? html`
${this.shouldShowHistory(domain)
? html`
<ha-icon-button
slot="actionItems"
.label=${this.hass.localize(
"ui.dialogs.more_info_control.history"
)}
.path=${mdiChartBoxOutline}
@click=${this._goToHistory}
></ha-icon-button>
`
: null}
<ha-icon-button
slot="actionItems"
.label=${this.hass.localize(
"ui.dialogs.more_info_control.edit"
"ui.dialogs.more_info_control.settings"
)}
.path=${mdiPencil}
@click=${this._gotoEdit}
.path=${mdiCogOutline}
@click=${this._goToSettings}
></ha-icon-button>
`
: ""}
</ha-header-bar>
${isAdmin
? html`<ha-button-menu
corner="BOTTOM_END"
menuCorner="END"
slot="actionItems"
@closed=${stopPropagation}
fixed
>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
${tabs.length > 1
? html`
<mwc-tab-bar
.activeIndex=${tabs.indexOf(this._currTab)}
@MDCTabBar:activated=${this._handleTabChanged}
>
${tabs.map(
(tab) => html`
<mwc-tab
.label=${this.hass.localize(
`ui.dialogs.more_info_control.${tab}`
)}
></mwc-tab>
`
)}
</mwc-tab-bar>
`
: ""}
${deviceId
? html`
<ha-list-item
graphic="icon"
@request-selected=${this._goToDevice}
>
${this.hass.localize(
"ui.dialogs.more_info_control.device_info"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiDevices}
></ha-svg-icon>
</ha-list-item>
`
: null}
${this.shouldShowEditIcon(domain, stateObj)
? html`
<ha-list-item
graphic="icon"
@request-selected=${this._goToEdit}
>
${this.hass.localize(
"ui.dialogs.more_info_control.edit"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiPencilOutline}
></ha-svg-icon>
</ha-list-item>
`
: null}
<ha-list-item
graphic="icon"
@request-selected=${this._goToRelated}
>
${this.hass.localize(
"ui.dialogs.more_info_control.related"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiInformationOutline}
></ha-svg-icon>
</ha-list-item>
</ha-button-menu>`
: null}
`
: null}
</ha-header-bar>
</div>
<div class="content" tabindex="-1" dialogInitialFocus>
${cache(
this._currTab === "info"
this._currView === "info"
? html`
<ha-more-info-info
dialogInitialFocus
.hass=${this.hass}
.entityId=${this._entityId}
></ha-more-info-info>
`
: this._currTab === "history"
: this._currView === "history"
? html`
<ha-more-info-history-and-logbook
.hass=${this.hass}
.entityId=${this._entityId}
></ha-more-info-history-and-logbook>
`
: this._currTab === "settings"
: this._currView === "settings"
? html`
<ha-more-info-settings
.hass=${this.hass}
@ -178,7 +318,6 @@ export class MoreInfoDialog extends LitElement {
`
: html`
<ha-related-items
class="content"
.hass=${this.hass}
.itemId=${entityId}
itemType="entity"
@ -195,83 +334,24 @@ export class MoreInfoDialog extends LitElement {
this.addEventListener("close-dialog", () => this.closeDialog());
}
protected willUpdate(changedProps: PropertyValues) {
super.willUpdate(changedProps);
if (!this._entityId) {
return;
}
const tabs = this._getTabs(this._entityId, this.hass.user!.is_admin);
if (!tabs.includes(this._currTab)) {
this._currTab = tabs[0];
}
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (changedProps.has("_currTab")) {
this.setAttribute("tab", this._currTab);
if (changedProps.has("_currView")) {
this.setAttribute("view", this._currView);
}
}
private _getTabs(entityId: string, isAdmin: boolean): Tab[] {
const domain = computeDomain(entityId);
const tabs: Tab[] = ["info"];
// Info and history are combined in info when there are no
// dedicated more-info controls. If not combined, add a history tab.
if (
DOMAINS_WITH_MORE_INFO.includes(domain) &&
(computeShowHistoryComponent(this.hass, entityId) ||
computeShowLogBookComponent(this.hass, entityId))
) {
tabs.push("history");
}
if (isAdmin) {
tabs.push("settings");
tabs.push("related");
}
return tabs;
}
private _enlarge() {
this.large = !this.large;
}
private _gotoEdit() {
const stateObj = this.hass.states[this._entityId!];
const domain = computeDomain(this._entityId!);
let idToPassThroughUrl = stateObj.entity_id;
if (EDITABLE_DOMAINS_WITH_ID.includes(domain) || domain === "person") {
idToPassThroughUrl = stateObj.attributes.id;
}
if (EDITABLE_DOMAINS_WITH_UNIQUE_ID.includes(domain)) {
idToPassThroughUrl = this.hass.entities[this._entityId!].unique_id;
}
navigate(`/config/${domain}/edit/${idToPassThroughUrl}`);
this.closeDialog();
}
private _handleTabChanged(ev: CustomEvent): void {
const newTab = this._getTabs(this._entityId!, this.hass.user!.is_admin)[
ev.detail.index
];
if (newTab === this._currTab) {
return;
}
this._currTab = newTab;
}
static get styles() {
return [
haStyleDialog,
css`
ha-dialog {
--dialog-surface-position: static;
--dialog-content-position: static;
--dialog-content-position: relative;
--vertical-align-dialog: flex-start;
--dialog-content-padding: 0;
--content-padding: 24px;
@ -282,17 +362,11 @@ export class MoreInfoDialog extends LitElement {
--mdc-theme-primary: var(--mdc-theme-surface);
flex-shrink: 0;
display: block;
border-bottom: none;
}
.content {
outline: none;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
ha-header-bar {
--mdc-theme-primary: var(--app-header-background-color);
--mdc-theme-on-primary: var(--app-header-text-color, white);
border-bottom: none;
}
}
.heading {
border-bottom: 1px solid
@ -303,14 +377,19 @@ export class MoreInfoDialog extends LitElement {
padding: var(--content-padding);
}
:host([tab="settings"]) ha-dialog {
:host([view="settings"]) ha-dialog {
--content-padding: 0;
}
:host([tab="info"]) ha-dialog[data-domain="camera"] {
:host([view="info"]) ha-dialog[data-domain="camera"] {
--content-padding: 0;
/* max height of the video is full screen, minus the height of the header of the dialog and the padding of the dialog (mdc-dialog-max-height: calc(100% - 72px)) */
--video-max-height: calc(100vh - 113px - 72px);
--video-max-height: calc(100vh - 65px - 72px);
}
.main-title {
overflow: hidden;
text-overflow: ellipsis;
}
@media all and (min-width: 600px) and (min-height: 501px) {
@ -322,8 +401,6 @@ export class MoreInfoDialog extends LitElement {
}
.main-title {
overflow: hidden;
text-overflow: ellipsis;
cursor: default;
}

View File

@ -482,14 +482,13 @@ class CloudGoogleAssistant extends LitElement {
private _showMoreInfo(ev) {
const entityId = ev.currentTarget.stateObj.entity_id;
const moreInfoTab = ev.currentTarget.moreInfoTab;
fireEvent(this, "hass-more-info", { entityId, tab: moreInfoTab });
fireEvent(this, "hass-more-info", { entityId });
}
private _showMoreInfoSettings(ev) {
ev.stopPropagation();
const entityId = ev.currentTarget.stateObj.entity_id;
fireEvent(this, "hass-more-info", { entityId, tab: "settings" });
fireEvent(this, "hass-more-info", { entityId, view: "settings" });
}
private async _exposeChanged(ev: CustomEvent<ActionDetail>) {

View File

@ -223,10 +223,7 @@ export class HaDeviceEntitiesCard extends LitElement {
private _openEditEntry(ev: Event): void {
const entry = (ev.currentTarget! as any).entry;
showMoreInfoDialog(this, {
entityId: entry.entity_id,
tab: "settings",
});
showMoreInfoDialog(this, { entityId: entry.entity_id });
}
private _addToLovelaceView(): void {

View File

@ -1247,10 +1247,7 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
if (!entity) {
return;
}
showMoreInfoDialog(parent, {
entityId: entity.entity_id,
tab: "settings",
});
showMoreInfoDialog(parent, { entityId: entity.entity_id });
});
}, "entity_registry_updated");
}

View File

@ -916,10 +916,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
private _openEditEntry(ev: CustomEvent): void {
const entityId = (ev.detail as RowClickedEvent).id;
showMoreInfoDialog(this, {
entityId,
tab: "settings",
});
showMoreInfoDialog(this, { entityId });
}
private async _loadConfigEntries() {

View File

@ -358,10 +358,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
private async _openEditDialog(ev: CustomEvent): Promise<void> {
const entityId = (ev.detail as RowClickedEvent).id;
showMoreInfoDialog(this, {
entityId,
tab: "settings",
});
showMoreInfoDialog(this, { entityId });
}
private _createHelpler() {

View File

@ -13,7 +13,6 @@ import "../../../components/ha-formfield";
import "../../../components/ha-header-bar";
import "../../../components/ha-icon-button";
import "../../../components/ha-radio";
import "../../../components/ha-related-items";
import "../../../components/ha-settings-row";
import "../../../components/ha-textfield";
import { extractApiErrorMessage } from "../../../data/hassio/common";

View File

@ -17,7 +17,6 @@ import "../../../components/ha-formfield";
import "../../../components/ha-header-bar";
import "../../../components/ha-icon-button";
import "../../../components/ha-radio";
import "../../../components/ha-related-items";
import { extractApiErrorMessage } from "../../../data/hassio/common";
import {
AccessPoints,

View File

@ -29,7 +29,7 @@ export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
"ha-more-info-dialog",
{
entityId: ev.detail.entityId,
tab: ev.detail.tab,
view: ev.detail.view || ev.detail.tab,
},
() => import("../dialogs/more-info/ha-more-info-dialog")
);

View File

@ -832,10 +832,11 @@
"settings": "Settings",
"edit": "Edit entity",
"details": "Details",
"info": "Info",
"back_to_info": "Back to info",
"related": "Related",
"history": "History",
"logbook": "Logbook",
"device_info": "Device info",
"last_changed": "Last changed",
"last_updated": "Last updated",
"show_more": "Show more",