Skip to content

Commit

Permalink
Improve element editor and migrate heading-entity editor (#22034)
Browse files Browse the repository at this point in the history
* Extract load config element

* Improve error by using ha-alert

* Create hui-hase-editor

* Migrate heading entity form to its own editor

* Rename editor

* Rename

* Rename

* Move heading entity to its own component

* Fix default action for heading entity
  • Loading branch information
piitaya authored Sep 24, 2024
1 parent a759767 commit c4a700a
Show file tree
Hide file tree
Showing 19 changed files with 532 additions and 575 deletions.
113 changes: 113 additions & 0 deletions src/panels/lovelace/cards/heading/hui-heading-entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import memoizeOne from "memoize-one";
import "../../../../components/ha-card";
import "../../../../components/ha-icon";
import "../../../../components/ha-icon-next";
import "../../../../components/ha-state-icon";
import { ActionHandlerEvent } from "../../../../data/lovelace/action_handler";
import "../../../../state-display/state-display";
import { HomeAssistant } from "../../../../types";
import { actionHandler } from "../../common/directives/action-handler-directive";
import { handleAction } from "../../common/handle-action";
import { hasAction } from "../../common/has-action";
import type { HeadingEntityConfig } from "../types";

@customElement("hui-heading-entity")
export class HuiHeadingEntity extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;

@property({ attribute: false }) public config!: HeadingEntityConfig | string;

private _handleAction(ev: ActionHandlerEvent) {
const config: HeadingEntityConfig = {
tap_action: {
action: "none",
},
...this._config(this.config),
};
handleAction(this, this.hass!, config, ev.detail.action!);
}

private _config = memoizeOne(
(configOrString: HeadingEntityConfig | string): HeadingEntityConfig => {
const config =
typeof configOrString === "string"
? { entity: configOrString }
: configOrString;

return {
tap_action: {
action: "none",
},
...config,
};
}
);

protected render() {
const config = this._config(this.config);

const stateObj = this.hass!.states[config.entity];

if (!stateObj) {
return nothing;
}

const actionable = hasAction(config.tap_action);

return html`
<div
class="entity"
@action=${this._handleAction}
.actionHandler=${actionHandler()}
role=${ifDefined(actionable ? "button" : undefined)}
tabindex=${ifDefined(actionable ? "0" : undefined)}
>
<ha-state-icon
.hass=${this.hass}
.icon=${config.icon}
.stateObj=${stateObj}
></ha-state-icon>
<state-display
.hass=${this.hass}
.stateObj=${stateObj}
.content=${config.content || "state"}
></state-display>
</div>
`;
}

static get styles(): CSSResultGroup {
return css`
[role="button"] {
cursor: pointer;
}
.entity {
display: flex;
flex-direction: row;
white-space: nowrap;
align-items: center;
gap: 3px;
color: var(--secondary-text-color);
font-family: Roboto;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 20px; /* 142.857% */
letter-spacing: 0.1px;
--mdc-icon-size: 14px;
}
.entity ha-state-icon {
--ha-icon-display: block;
}
`;
}
}

declare global {
interface HTMLElementTagNameMap {
"hui-heading-entity": HuiHeadingEntity;
}
}
76 changes: 7 additions & 69 deletions src/panels/lovelace/cards/hui-heading-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import type {
LovelaceCardEditor,
LovelaceLayoutOptions,
} from "../types";
import type { HeadingCardConfig, HeadingCardEntityConfig } from "./types";
import "./heading/hui-heading-entity";
import type { HeadingCardConfig } from "./types";

@customElement("hui-heading-card")
export class HuiHeadingCard extends LitElement implements LovelaceCard {
Expand Down Expand Up @@ -91,8 +92,11 @@ export class HuiHeadingCard extends LitElement implements LovelaceCard {
${this._config.entities?.length
? html`
<div class="entities">
${this._config.entities.map((config) =>
this._renderEntity(config)
${this._config.entities.map(
(config) => html`
<hui-heading-entity .config=${config} .hass=${this.hass}>
</hui-heading-entity>
`
)}
</div>
`
Expand All @@ -102,54 +106,6 @@ export class HuiHeadingCard extends LitElement implements LovelaceCard {
`;
}

private _handleEntityAction(ev: ActionHandlerEvent) {
const config = {
tap_action: {
action: "none",
},
...(ev.currentTarget as any).config,
};

handleAction(this, this.hass!, config, ev.detail.action!);
}

_renderEntity(entityConfig: string | HeadingCardEntityConfig) {
const config =
typeof entityConfig === "string"
? { entity: entityConfig }
: entityConfig;

const stateObj = this.hass!.states[config.entity];

if (!stateObj) {
return nothing;
}

const actionable = hasAction(config.tap_action || { action: "none" });

return html`
<div
.config=${config}
class="entity"
@action=${this._handleEntityAction}
.actionHandler=${actionHandler()}
role=${ifDefined(actionable ? "button" : undefined)}
tabindex=${ifDefined(actionable ? "0" : undefined)}
>
<ha-state-icon
.hass=${this.hass}
.icon=${config.icon}
.stateObj=${stateObj}
></ha-state-icon>
<state-display
.hass=${this.hass}
.stateObj=${stateObj}
.content=${config.content || "state"}
></state-display>
</div>
`;
}

static get styles(): CSSResultGroup {
return css`
ha-card {
Expand Down Expand Up @@ -231,24 +187,6 @@ export class HuiHeadingCard extends LitElement implements LovelaceCard {
justify-content: flex-end;
gap: 4px 10px;
}
.entities .entity {
display: flex;
flex-direction: row;
white-space: nowrap;
align-items: center;
gap: 3px;
color: var(--secondary-text-color);
font-family: Roboto;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 20px; /* 142.857% */
letter-spacing: 0.1px;
--mdc-icon-size: 14px;
}
.entities .entity ha-state-icon {
--ha-icon-display: block;
}
`;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/panels/lovelace/cards/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ export interface TileCardConfig extends LovelaceCardConfig {
features?: LovelaceCardFeatureConfig[];
}

export interface HeadingCardEntityConfig {
export interface HeadingEntityConfig {
entity: string;
content?: string | string[];
icon?: string;
Expand All @@ -515,5 +515,5 @@ export interface HeadingCardConfig extends LovelaceCardConfig {
heading?: string;
icon?: string;
tap_action?: ActionConfig;
entities?: (string | HeadingCardEntityConfig)[];
entities?: (string | HeadingEntityConfig)[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { customElement, state } from "lit/decorators";
import { LovelaceBadgeConfig } from "../../../../data/lovelace/config/badge";
import { getBadgeElementClass } from "../../create-element/create-badge-element";
import type { LovelaceCardEditor, LovelaceConfigForm } from "../../types";
import { HuiElementEditor } from "../hui-element-editor";
import { HuiTypedElementEditor } from "../hui-typed-element-editor";
import "./hui-badge-visibility-editor";

const tabs = ["config", "visibility"] as const;

@customElement("hui-badge-element-editor")
export class HuiBadgeElementEditor extends HuiElementEditor<LovelaceBadgeConfig> {
export class HuiBadgeElementEditor extends HuiTypedElementEditor<LovelaceBadgeConfig> {
@state() private _currTab: (typeof tabs)[number] = tabs[0];

protected async getConfigElement(): Promise<LovelaceCardEditor | undefined> {
Expand Down Expand Up @@ -88,7 +88,7 @@ export class HuiBadgeElementEditor extends HuiElementEditor<LovelaceBadgeConfig>

static get styles(): CSSResultGroup {
return [
HuiElementEditor.styles,
HuiTypedElementEditor.styles,
css`
mwc-tab-bar {
text-transform: uppercase;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import { customElement, property, state } from "lit/decorators";
import { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import { getCardElementClass } from "../../create-element/create-card-element";
import type { LovelaceCardEditor, LovelaceConfigForm } from "../../types";
import { HuiElementEditor } from "../hui-element-editor";
import { HuiTypedElementEditor } from "../hui-typed-element-editor";
import "./hui-card-layout-editor";
import "./hui-card-visibility-editor";
import { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";

const tabs = ["config", "visibility", "layout"] as const;

@customElement("hui-card-element-editor")
export class HuiCardElementEditor extends HuiElementEditor<LovelaceCardConfig> {
export class HuiCardElementEditor extends HuiTypedElementEditor<LovelaceCardConfig> {
@property({ type: Boolean, attribute: "show-visibility-tab" })
public showVisibilityTab = false;

Expand Down Expand Up @@ -119,7 +119,7 @@ export class HuiCardElementEditor extends HuiElementEditor<LovelaceCardConfig> {

static get styles(): CSSResultGroup {
return [
HuiElementEditor.styles,
HuiTypedElementEditor.styles,
css`
mwc-tab-bar {
text-transform: uppercase;
Expand Down
Loading

0 comments on commit c4a700a

Please sign in to comment.