Add buttons header-footer (#4601)

* Add buttons header-footer

* Simplify

* Update src/panels/lovelace/header-footer/hui-buttons-header-footer.ts

Co-Authored-By: Ian Richardson <iantrich@gmail.com>

* Address comments

Co-authored-by: Ian Richardson <iantrich@gmail.com>
This commit is contained in:
Paulus Schoutsen 2020-01-27 00:34:08 -08:00 committed by Bram Kragten
parent 95ba1fd0cb
commit 82fb622904
7 changed files with 140 additions and 10 deletions

View File

@ -20,7 +20,7 @@ import { computeActiveState } from "../../common/entity/compute_active_state";
import { ifDefined } from "lit-html/directives/if-defined";
import { iconColorCSS } from "../../common/style/icon_color_css";
class StateBadge extends LitElement {
export class StateBadge extends LitElement {
public hass?: HomeAssistant;
@property() public stateObj?: HassEntity;
@property() public overrideIcon?: string;

View File

@ -14,7 +14,11 @@ import "../components/hui-entities-toggle";
import { HomeAssistant } from "../../../types";
import { LovelaceRow } from "../entity-rows/types";
import { LovelaceCard, LovelaceCardEditor } from "../types";
import {
LovelaceCard,
LovelaceCardEditor,
LovelaceHeaderFooter,
} from "../types";
import { processConfigEntities } from "../common/process-config-entities";
import { createRowElement } from "../create-element/create-row-element";
import { EntitiesCardConfig, EntitiesCardEntityConfig } from "./types";
@ -48,6 +52,11 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
(element as LovelaceRow).hass = hass;
}
);
this.shadowRoot!.querySelectorAll(".header-footer > *").forEach(
(element: unknown) => {
(element as LovelaceHeaderFooter).hass = hass;
}
);
const entitiesToggle = this.shadowRoot!.querySelector(
"hui-entities-toggle"
);
@ -135,6 +144,7 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
this.renderEntity(entityConf)
)}
</div>
${this._config.footer
? this.renderHeaderFooter(this._config.footer, "footer")
: ""}
@ -192,7 +202,7 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
element.hass = this._hass;
}
return html`
<div class=${className}>${element}</div>
<div class=${"header-footer " + className}>${element}</div>
`;
}

View File

@ -1,9 +1,8 @@
import { computeStateName } from "../../../common/entity/compute_state_name";
import { HomeAssistant } from "../../../types";
import { LovelaceElementConfig } from "../elements/types";
import { ActionConfig } from "../../../data/lovelace";
interface Config extends LovelaceElementConfig {
interface Config {
entity?: string;
title?: string;
tap_action?: ActionConfig;
@ -30,6 +29,10 @@ export const computeTooltip = (hass: HomeAssistant, config: Config): string => {
: config.entity;
}
if (!config.tap_action && !config.hold_action) {
return stateName;
}
const tapTooltip = config.tap_action
? computeActionTooltip(hass, stateName, config.tap_action, false)
: "";

View File

@ -1,8 +1,9 @@
import "../header-footer/hui-picture-header-footer";
import "../header-footer/hui-buttons-header-footer";
import { LovelaceHeaderFooterConfig } from "../header-footer/types";
import { createLovelaceElement } from "./create-element-base";
const SPECIAL_TYPES = new Set(["picture"]);
const SPECIAL_TYPES = new Set(["picture", "buttons"]);
export const createHeaderFooterElement = (config: LovelaceHeaderFooterConfig) =>
createLovelaceElement("header-footer", config, SPECIAL_TYPES);

View File

@ -31,7 +31,7 @@ import {
EntitiesCardConfig,
EntitiesCardEntityConfig,
} from "../../cards/types";
import { pictureHeaderFooterConfigStruct } from "../../header-footer/types";
import { headerFooterConfigStructs } from "../../header-footer/types";
const cardConfigStruct = struct({
type: "string",
@ -39,8 +39,8 @@ const cardConfigStruct = struct({
theme: "string?",
show_header_toggle: "boolean?",
entities: [entitiesConfigStruct],
header: struct.optional(pictureHeaderFooterConfigStruct),
footer: struct.optional(pictureHeaderFooterConfigStruct),
header: struct.optional(headerFooterConfigStructs),
footer: struct.optional(headerFooterConfigStructs),
});
@customElement("hui-entities-card-editor")

View File

@ -0,0 +1,102 @@
import {
html,
LitElement,
TemplateResult,
customElement,
css,
CSSResult,
queryAll,
} from "lit-element";
import "@material/mwc-ripple";
import "../../../components/entity/state-badge";
import "../../../components/ha-card";
import "../../../components/ha-icon";
import "../components/hui-warning-element";
import { HomeAssistant } from "../../../types";
import { LovelaceHeaderFooter } from "../types";
import { ButtonsHeaderFooterConfig } from "./types";
import { EntityConfig } from "../entity-rows/types";
import { processConfigEntities } from "../common/process-config-entities";
import { toggleEntity } from "../common/entity/toggle-entity";
import { computeTooltip } from "../common/compute-tooltip";
// tslint:disable-next-line: no-duplicate-imports
import { StateBadge } from "../../../components/entity/state-badge";
@customElement("hui-buttons-header-footer")
export class HuiButtonsHeaderFooter extends LitElement
implements LovelaceHeaderFooter {
public static getStubConfig(): object {
return { entities: [] };
}
private _configEntities?: EntityConfig[];
private _hass?: HomeAssistant;
@queryAll("state-badge") private _badges!: StateBadge[];
public setConfig(config: ButtonsHeaderFooterConfig): void {
this._configEntities = processConfigEntities(config.entities);
this.requestUpdate();
}
set hass(hass: HomeAssistant) {
this._hass = hass;
this._badges.forEach((badge, index: number) => {
badge.hass = hass;
badge.stateObj = hass.states[this._configEntities![index].entity];
});
}
protected render(): TemplateResult | void {
return html`
${(this._configEntities || []).map((entityConf) => {
const stateObj = this._hass!.states[entityConf.entity];
if (!stateObj) {
return html`<div class='missing'><iron-icon icon="hass:alert"></div>`;
}
return html`
<div>
<state-badge
title=${computeTooltip(this._hass!, entityConf)}
@click=${this._toggle}
.hass=${this._hass}
.stateObj=${stateObj}
.overrideIcon=${entityConf.icon}
.overrideImage=${entityConf.image}
stateColor
tabindex="0"
></state-badge>
<mwc-ripple unbounded></mwc-ripple>
</div>
`;
})}
`;
}
private async _toggle(ev) {
await toggleEntity(this._hass!, ev.target.stateObj.entity_id);
}
static get styles(): CSSResult {
return css`
:host {
display: flex;
justify-content: space-evenly;
}
.missing {
color: #fce588;
}
state-badge {
cursor: pointer;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-buttons-header-footer": HuiButtonsHeaderFooter;
}
}

View File

@ -1,11 +1,15 @@
import { ActionConfig } from "../../../data/lovelace";
import { struct } from "../common/structs/struct";
import { actionConfigStruct } from "../editor/types";
import { actionConfigStruct, entitiesConfigStruct } from "../editor/types";
export interface LovelaceHeaderFooterConfig {
type: string;
}
export interface ButtonsHeaderFooterConfig extends LovelaceHeaderFooterConfig {
entities: string[];
}
export interface PictureHeaderFooterConfig extends LovelaceHeaderFooterConfig {
image: string;
tap_action?: ActionConfig;
@ -20,3 +24,13 @@ export const pictureHeaderFooterConfigStruct = struct({
hold_action: struct.optional(actionConfigStruct),
double_tap_action: struct.optional(actionConfigStruct),
});
export const buttonsHeaderFooterConfigStruct = struct({
type: "string",
entities: [entitiesConfigStruct],
});
export const headerFooterConfigStructs = struct.union([
pictureHeaderFooterConfigStruct,
buttonsHeaderFooterConfigStruct,
]);