ha-frontend/src/layouts/hass-subpage.ts

187 lines
4.8 KiB
TypeScript

import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, eventOptions, property } from "lit/decorators";
import { restoreScroll } from "../common/decorators/restore-scroll";
import { toggleAttribute } from "../common/dom/toggle_attribute";
import { computeRTL } from "../common/util/compute_rtl";
import "../components/ha-icon-button-arrow-prev";
import "../components/ha-menu-button";
import { HomeAssistant } from "../types";
@customElement("hass-subpage")
class HassSubpage extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public header?: string;
@property({ type: Boolean, attribute: "main-page" }) public mainPage = false;
@property({ type: String, attribute: "back-path" }) public backPath?: string;
@property() public backCallback?: () => void;
@property({ type: Boolean, reflect: true }) public narrow = false;
@property({ type: Boolean }) public supervisor = false;
// @ts-ignore
@restoreScroll(".content") private _savedScrollPos?: number;
protected willUpdate(changedProps: PropertyValues): void {
super.willUpdate(changedProps);
if (!changedProps.has("hass")) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass.locale) {
toggleAttribute(this, "rtl", computeRTL(this.hass));
}
}
protected render(): TemplateResult {
return html`
<div class="toolbar">
${this.mainPage || history.state?.root
? html`
<ha-menu-button
.hassio=${this.supervisor}
.hass=${this.hass}
.narrow=${this.narrow}
></ha-menu-button>
`
: this.backPath
? html`
<a href=${this.backPath}>
<ha-icon-button-arrow-prev
.hass=${this.hass}
></ha-icon-button-arrow-prev>
</a>
`
: html`
<ha-icon-button-arrow-prev
.hass=${this.hass}
@click=${this._backTapped}
></ha-icon-button-arrow-prev>
`}
<div class="main-title"><slot name="header">${this.header}</slot></div>
<slot name="toolbar-icon"></slot>
</div>
<div class="content" @scroll=${this._saveScrollPos}><slot></slot></div>
<div id="fab">
<slot name="fab"></slot>
</div>
`;
}
@eventOptions({ passive: true })
private _saveScrollPos(e: Event) {
this._savedScrollPos = (e.target as HTMLDivElement).scrollTop;
}
private _backTapped(): void {
if (this.backCallback) {
this.backCallback();
return;
}
history.back();
}
static get styles(): CSSResultGroup {
return css`
:host {
display: block;
height: 100%;
background-color: var(--primary-background-color);
overflow: hidden;
position: relative;
}
:host([narrow]) {
width: 100%;
position: fixed;
}
.toolbar {
display: flex;
align-items: center;
font-size: 20px;
height: var(--header-height);
padding: 8px 12px;
pointer-events: none;
background-color: var(--app-header-background-color);
font-weight: 400;
color: var(--app-header-text-color, white);
border-bottom: var(--app-header-border-bottom, none);
box-sizing: border-box;
}
@media (max-width: 599px) {
.toolbar {
padding: 4px;
}
}
.toolbar a {
color: var(--sidebar-text-color);
text-decoration: none;
}
ha-menu-button,
ha-icon-button-arrow-prev,
::slotted([slot="toolbar-icon"]) {
pointer-events: auto;
color: var(--sidebar-icon-color);
}
.main-title {
margin: 0 0 0 24px;
line-height: 20px;
flex-grow: 1;
}
.content {
position: relative;
width: 100%;
height: calc(100% - 1px - var(--header-height));
overflow-y: auto;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
#fab {
position: absolute;
right: calc(16px + env(safe-area-inset-right));
bottom: calc(16px + env(safe-area-inset-bottom));
z-index: 1;
}
:host([narrow]) #fab.tabs {
bottom: calc(84px + env(safe-area-inset-bottom));
}
#fab[is-wide] {
bottom: 24px;
right: 24px;
}
:host([rtl]) #fab {
right: auto;
left: calc(16px + env(safe-area-inset-left));
}
:host([rtl][is-wide]) #fab {
bottom: 24px;
left: 24px;
right: auto;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"hass-subpage": HassSubpage;
}
}