forked from home-assistant/frontend
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new badges design with UI editor (home-assistant#21401)
* Add new entity badge * Improve badge render * Add edit mode * Add editor * Increase height * Use hui-badge * Add editor * Add drag and drop * Fix editor translations * Fix icon * Fix inactive color * Add state content * Add default config * Fix types * Add custom badge support to editor * Fix custom badges * Add new badges to masonry view * fix lint * Fix inactive color * Fix entity filter card * Add display type option * Add support for picture * Improve focus style * Add visibility editor * Fix visibility * Fix add/delete card inside section * Fix translations * Add error badge * Rename classes * Fix badge type * Remove badges from section type * Add missing types
- Loading branch information
Showing
32 changed files
with
2,460 additions
and
180 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,12 @@ | ||
import { Condition } from "../../../panels/lovelace/common/validate-condition"; | ||
|
||
export interface LovelaceBadgeConfig { | ||
type?: string; | ||
[key: string]: any; | ||
visibility?: Condition[]; | ||
} | ||
|
||
export const defaultBadgeConfig = (entity_id: string): LovelaceBadgeConfig => ({ | ||
type: "entity", | ||
entity: entity_id, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
import { PropertyValues, ReactiveElement } from "lit"; | ||
import { customElement, property } from "lit/decorators"; | ||
import { fireEvent } from "../../../common/dom/fire_event"; | ||
import { MediaQueriesListener } from "../../../common/dom/media_query"; | ||
import "../../../components/ha-svg-icon"; | ||
import { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge"; | ||
import type { HomeAssistant } from "../../../types"; | ||
import { | ||
attachConditionMediaQueriesListeners, | ||
checkConditionsMet, | ||
} from "../common/validate-condition"; | ||
import { createBadgeElement } from "../create-element/create-badge-element"; | ||
import { createErrorBadgeConfig } from "../create-element/create-element-base"; | ||
import type { LovelaceBadge } from "../types"; | ||
|
||
declare global { | ||
interface HASSDomEvents { | ||
"badge-updated": undefined; | ||
} | ||
} | ||
|
||
@customElement("hui-badge") | ||
export class HuiBadge extends ReactiveElement { | ||
@property({ type: Boolean }) public preview = false; | ||
|
||
@property({ attribute: false }) public config?: LovelaceBadgeConfig; | ||
|
||
@property({ attribute: false }) public hass?: HomeAssistant; | ||
|
||
private _elementConfig?: LovelaceBadgeConfig; | ||
|
||
public load() { | ||
if (!this.config) { | ||
throw new Error("Cannot build badge without config"); | ||
} | ||
this._loadElement(this.config); | ||
} | ||
|
||
private _element?: LovelaceBadge; | ||
|
||
private _listeners: MediaQueriesListener[] = []; | ||
|
||
protected createRenderRoot() { | ||
return this; | ||
} | ||
|
||
public disconnectedCallback() { | ||
super.disconnectedCallback(); | ||
this._clearMediaQueries(); | ||
} | ||
|
||
public connectedCallback() { | ||
super.connectedCallback(); | ||
this._listenMediaQueries(); | ||
this._updateVisibility(); | ||
} | ||
|
||
private _updateElement(config: LovelaceBadgeConfig) { | ||
if (!this._element) { | ||
return; | ||
} | ||
this._element.setConfig(config); | ||
this._elementConfig = config; | ||
fireEvent(this, "badge-updated"); | ||
} | ||
|
||
private _loadElement(config: LovelaceBadgeConfig) { | ||
this._element = createBadgeElement(config); | ||
this._elementConfig = config; | ||
if (this.hass) { | ||
this._element.hass = this.hass; | ||
} | ||
this._element.addEventListener( | ||
"ll-upgrade", | ||
(ev: Event) => { | ||
ev.stopPropagation(); | ||
if (this.hass) { | ||
this._element!.hass = this.hass; | ||
} | ||
fireEvent(this, "badge-updated"); | ||
}, | ||
{ once: true } | ||
); | ||
this._element.addEventListener( | ||
"ll-rebuild", | ||
(ev: Event) => { | ||
ev.stopPropagation(); | ||
this._loadElement(config); | ||
fireEvent(this, "badge-updated"); | ||
}, | ||
{ once: true } | ||
); | ||
while (this.lastChild) { | ||
this.removeChild(this.lastChild); | ||
} | ||
this._updateVisibility(); | ||
} | ||
|
||
protected willUpdate(changedProps: PropertyValues<typeof this>): void { | ||
super.willUpdate(changedProps); | ||
|
||
if (!this._element) { | ||
this.load(); | ||
} | ||
} | ||
|
||
protected update(changedProps: PropertyValues<typeof this>) { | ||
super.update(changedProps); | ||
|
||
if (this._element) { | ||
if (changedProps.has("config")) { | ||
const elementConfig = this._elementConfig; | ||
if (this.config !== elementConfig && this.config) { | ||
const typeChanged = this.config?.type !== elementConfig?.type; | ||
if (typeChanged) { | ||
this._loadElement(this.config); | ||
} else { | ||
this._updateElement(this.config); | ||
} | ||
} | ||
} | ||
if (changedProps.has("hass")) { | ||
try { | ||
if (this.hass) { | ||
this._element.hass = this.hass; | ||
} | ||
} catch (e: any) { | ||
this._loadElement(createErrorBadgeConfig(e.message, null)); | ||
} | ||
} | ||
} | ||
|
||
if (changedProps.has("hass") || changedProps.has("preview")) { | ||
this._updateVisibility(); | ||
} | ||
} | ||
|
||
private _clearMediaQueries() { | ||
this._listeners.forEach((unsub) => unsub()); | ||
this._listeners = []; | ||
} | ||
|
||
private _listenMediaQueries() { | ||
this._clearMediaQueries(); | ||
if (!this.config?.visibility) { | ||
return; | ||
} | ||
const conditions = this.config.visibility; | ||
const hasOnlyMediaQuery = | ||
conditions.length === 1 && | ||
conditions[0].condition === "screen" && | ||
!!conditions[0].media_query; | ||
|
||
this._listeners = attachConditionMediaQueriesListeners( | ||
this.config.visibility, | ||
(matches) => { | ||
this._updateVisibility(hasOnlyMediaQuery && matches); | ||
} | ||
); | ||
} | ||
|
||
private _updateVisibility(forceVisible?: boolean) { | ||
if (!this._element || !this.hass) { | ||
return; | ||
} | ||
|
||
if (this._element.hidden) { | ||
this._setElementVisibility(false); | ||
return; | ||
} | ||
|
||
const visible = | ||
forceVisible || | ||
this.preview || | ||
!this.config?.visibility || | ||
checkConditionsMet(this.config.visibility, this.hass); | ||
this._setElementVisibility(visible); | ||
} | ||
|
||
private _setElementVisibility(visible: boolean) { | ||
if (!this._element) return; | ||
|
||
if (this.hidden !== !visible) { | ||
this.style.setProperty("display", visible ? "" : "none"); | ||
this.toggleAttribute("hidden", !visible); | ||
} | ||
|
||
if (!visible && this._element.parentElement) { | ||
this.removeChild(this._element); | ||
} else if (visible && !this._element.parentElement) { | ||
this.appendChild(this._element); | ||
} | ||
} | ||
} | ||
|
||
declare global { | ||
interface HTMLElementTagNameMap { | ||
"hui-badge": HuiBadge; | ||
} | ||
} |
Oops, something went wrong.