Add support for submenus to reflect HA state.
This commit is contained in:
+15
-60
@@ -1,16 +1,14 @@
|
||||
import { HassEntity } from 'home-assistant-js-websocket';
|
||||
import { HomeAssistant, handleAction, hasAction, stateIcon } from 'custom-card-helpers';
|
||||
import { HomeAssistant, handleAction, hasAction } from 'custom-card-helpers';
|
||||
import {
|
||||
CSSResultGroup,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
html,
|
||||
unsafeCSS,
|
||||
PropertyValues,
|
||||
} from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { actionHandler } from '../action-handler-directive.js';
|
||||
|
||||
@@ -21,11 +19,12 @@ import type {
|
||||
ExtendedHomeAssistant,
|
||||
MenuButton,
|
||||
MenuConfig,
|
||||
StateParameters,
|
||||
} from '../types.js';
|
||||
import {
|
||||
convertActionToFrigateCardCustomAction,
|
||||
getActionConfigGivenAction,
|
||||
shouldUpdateBasedOnHass,
|
||||
refreshDynamicStateParameters,
|
||||
} from '../common.js';
|
||||
|
||||
import menuStyle from '../scss/menu.scss';
|
||||
@@ -86,7 +85,7 @@ export class FrigateCardMenu extends LitElement {
|
||||
|
||||
const interaction: string = ev.detail.action;
|
||||
const action = getActionConfigGivenAction(interaction, config);
|
||||
if (!config || !action || !interaction) {
|
||||
if (!config || !interaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,39 +108,6 @@ export class FrigateCardMenu extends LitElement {
|
||||
handleAction(this, this.hass as HomeAssistant, config, interaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the menu should be updated.
|
||||
* @param changedProps The changed properties.
|
||||
* @returns `true` if the menu should be updated, otherwise `false`.
|
||||
*/
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
const oldHass = changedProps.get('hass') as HomeAssistant | undefined;
|
||||
|
||||
if (changedProps.size > 1 || !oldHass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Extract the entities the menu rendering depends on (if any).
|
||||
const entities: string[] = [];
|
||||
for (let i = 0; i < this.buttons.length; i++) {
|
||||
const button = this.buttons[i];
|
||||
if (button.type == 'custom:frigate-card-menu-state-icon') {
|
||||
entities.push(button.entity);
|
||||
}
|
||||
}
|
||||
return shouldUpdateBasedOnHass(this.hass, oldHass, entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the style of emphasized menu items.
|
||||
* @returns A StyleInfo.
|
||||
*/
|
||||
public static getEmphasizedStyle(): StyleInfo {
|
||||
return {
|
||||
color: 'var(--primary-color, white)',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a button.
|
||||
* @param button The button configuration to render.
|
||||
@@ -150,19 +116,17 @@ export class FrigateCardMenu extends LitElement {
|
||||
protected _renderButton(button: MenuButton): TemplateResult | void {
|
||||
if (button.type == 'custom:frigate-card-menu-submenu') {
|
||||
return html` <frigate-card-submenu
|
||||
.hass=${this.hass}
|
||||
.submenu=${button}
|
||||
@action=${this._actionHandler.bind(this)}
|
||||
>
|
||||
</frigate-card-submenu>`;
|
||||
}
|
||||
|
||||
let state: HassEntity | null = null;
|
||||
let title = button.title;
|
||||
let icon = button.icon;
|
||||
let style = button.style || {};
|
||||
let stateParameters: StateParameters = {...button};
|
||||
|
||||
if (icon == FRIGATE_BUTTON_MENU_ICON) {
|
||||
icon =
|
||||
if (stateParameters.icon == FRIGATE_BUTTON_MENU_ICON) {
|
||||
stateParameters.icon =
|
||||
this._menuConfig?.mode.startsWith('hidden-') && !this.expand
|
||||
? 'mdi:alpha-f-box-outline'
|
||||
: 'mdi:alpha-f-box';
|
||||
@@ -172,16 +136,7 @@ export class FrigateCardMenu extends LitElement {
|
||||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
state = this.hass.states[button.entity];
|
||||
if (
|
||||
!!state &&
|
||||
button.state_color &&
|
||||
['on', 'active', 'home'].includes(state.state)
|
||||
) {
|
||||
style = { ...style, ...FrigateCardMenu.getEmphasizedStyle() };
|
||||
}
|
||||
title = title ?? (state?.attributes?.friendly_name || button.entity);
|
||||
icon = icon ?? stateIcon(state);
|
||||
stateParameters = refreshDynamicStateParameters(this.hass, stateParameters);
|
||||
}
|
||||
|
||||
const hasHold = hasAction(button.hold_action);
|
||||
@@ -197,17 +152,17 @@ export class FrigateCardMenu extends LitElement {
|
||||
// - title (replaced with .label)
|
||||
return html` <ha-icon-button
|
||||
class="${classMap(classes)}"
|
||||
style="${styleMap(style)}"
|
||||
icon=${icon || 'mdi:gesture-tap-button'}
|
||||
.label=${title || ''}
|
||||
title=${title || ''}
|
||||
style="${styleMap(stateParameters.style || {})}"
|
||||
icon=${stateParameters.icon || 'mdi:gesture-tap-button'}
|
||||
.label=${stateParameters.title || ''}
|
||||
title=${stateParameters.title || ''}
|
||||
@action=${(ev) => this._actionHandler(ev, button)}
|
||||
.actionHandler=${actionHandler({
|
||||
hasHold: hasHold,
|
||||
hasDoubleClick: hasDoubleClick,
|
||||
})}
|
||||
>
|
||||
<ha-icon icon="${icon || 'mdi:gesture-tap-button'}"></ha-icon>
|
||||
<ha-icon icon="${stateParameters.icon || 'mdi:gesture-tap-button'}"></ha-icon>
|
||||
</ha-icon-button>`;
|
||||
}
|
||||
|
||||
|
||||
+42
-30
@@ -1,22 +1,60 @@
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators';
|
||||
import { hasAction, HomeAssistant } from 'custom-card-helpers';
|
||||
import { styleMap } from 'lit/directives/style-map';
|
||||
|
||||
import { ExtendedHomeAssistant, MenuSubmenu, MenuSubmenuItem } from '../types.js';
|
||||
import { actionHandler } from '../action-handler-directive.js';
|
||||
import { MenuSubmenu } from '../types.js';
|
||||
import { refreshDynamicStateParameters } from '../common.js';
|
||||
|
||||
import submenuStyle from '../scss/submenu.scss';
|
||||
import { hasAction } from 'custom-card-helpers';
|
||||
import { styleMap } from 'lit/directives/style-map';
|
||||
|
||||
@customElement('frigate-card-submenu')
|
||||
export class FrigateCardSubmenu extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public hass?: HomeAssistant & ExtendedHomeAssistant;
|
||||
|
||||
@property({ attribute: false })
|
||||
public submenu?: MenuSubmenu;
|
||||
|
||||
protected _renderItem(item: MenuSubmenuItem): TemplateResult | void {
|
||||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
const stateParameters = refreshDynamicStateParameters(this.hass, {...item});
|
||||
|
||||
return html`
|
||||
<mwc-list-item
|
||||
style="${styleMap(stateParameters.style || {})}"
|
||||
graphic="icon"
|
||||
aria-label="${stateParameters.title || ''}"
|
||||
@action=${(ev) => {
|
||||
// Attach the action config so ascendants have access to it.
|
||||
ev.detail.config = item;
|
||||
}}
|
||||
.actionHandler=${actionHandler({
|
||||
hasHold: hasAction(item.hold_action),
|
||||
hasDoubleClick: hasAction(item.double_tap_action),
|
||||
})}
|
||||
>
|
||||
${stateParameters.title || ''}
|
||||
${stateParameters.icon
|
||||
? html` <ha-icon
|
||||
style="${styleMap(stateParameters.style || {})}"
|
||||
slot="graphic"
|
||||
icon="${stateParameters.icon}"
|
||||
>
|
||||
</ha-icon>`
|
||||
: ``}
|
||||
</mwc-list-item>
|
||||
`;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.submenu) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-button-menu corner="BOTTOM_LEFT">
|
||||
<ha-icon-button
|
||||
@@ -31,33 +69,7 @@ export class FrigateCardSubmenu extends LitElement {
|
||||
>
|
||||
<ha-icon icon="${this.submenu.icon}"></ha-icon>
|
||||
</ha-icon-button>
|
||||
${this.submenu.items.map(
|
||||
(item) => html`
|
||||
<mwc-list-item
|
||||
style="${styleMap(item.style || {})}"
|
||||
graphic="icon"
|
||||
aria-label="${item.title || ''}"
|
||||
@action=${(ev) => {
|
||||
// Attach the action config so ascendants have access to it.
|
||||
ev.detail.config = item;
|
||||
}}
|
||||
.actionHandler=${actionHandler({
|
||||
hasHold: hasAction(item.hold_action),
|
||||
hasDoubleClick: hasAction(item.double_tap_action),
|
||||
})}
|
||||
>
|
||||
${item.title || ''}
|
||||
${item.icon
|
||||
? html` <ha-icon
|
||||
style="${styleMap(item.style || {})}"
|
||||
slot="graphic"
|
||||
icon="${item.icon}"
|
||||
>
|
||||
</ha-icon>`
|
||||
: ``}
|
||||
</mwc-list-item>
|
||||
`,
|
||||
)}
|
||||
${this.submenu.items.map(this._renderItem.bind(this))}
|
||||
</ha-button-menu>
|
||||
`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user