import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { styleMap } from 'lit/directives/style-map.js'; import { actionHandler } from '../action-handler-directive.js'; import { MenuController } from '../components-lib/menu-controller.js'; import { MenuItem } from '../config/schema/elements/custom/menu/types.js'; import { MenuConfig } from '../config/schema/menu.js'; import { getEntityTitle } from '../ha/get-entity-title.js'; import { EntityRegistryManager } from '../ha/registry/entity/types.js'; import { HomeAssistant } from '../ha/types.js'; import menuStyle from '../scss/menu.scss'; import { hasAction } from '../utils/action.js'; import './icon.js'; import './submenu/select-button.js'; import './submenu/submenu-button'; @customElement('advanced-camera-card-menu') export class AdvancedCameraCardMenu extends LitElement { protected _controller = new MenuController(this); @property({ attribute: false }) public entityRegistryManager?: EntityRegistryManager; @property({ attribute: false }) public hass?: HomeAssistant; set menuConfig(menuConfig: MenuConfig) { this._controller.setMenuConfig(menuConfig); } set buttons(buttons: MenuItem[]) { this._controller.setButtons(buttons); } set expanded(expanded: boolean) { this._controller.setExpanded(expanded); } public toggleMenu(): void { this._controller.toggleExpanded(); } protected _renderButton(button: MenuItem): TemplateResult | void { if (!this.hass) { return; } if (button.type === 'custom:advanced-camera-card-menu-submenu') { return html` this._controller.handleAction(ev)} > `; } else if (button.type === 'custom:advanced-camera-card-menu-submenu-select') { return html` this._controller.handleAction(ev)} > `; } const title = this.hass && button.type === 'custom:advanced-camera-card-menu-state-icon' && !button.title ? getEntityTitle(this.hass, button.entity) : button.title; return html` this._controller.handleAction(ev, button)} > `; } /** Theme-related styling is dynamically injected into the menu depending on * the configured position, style and alignment to allow precise theming. * The alternative is a massive (post-sass processing) CSS file would need to * be shipped to account for every possible combination. * * Each rule uses 'var' values that have nested fallbacks of decreasing * specificity, so the most specific theme variable will match, followed by * the next most specific, etc. */ protected _renderPerInstanceStyle(): TemplateResult | void { const config = this._controller.getMenuConfig(); if (!config) { return; } const position = config.position; const style = config.style; const alignment = config.alignment; const generateValue = (suffix: string): string => { return ` var(--advanced-camera-card-menu-override-${suffix}, var(--advanced-camera-card-menu-position-${position}-alignment-${alignment}-style-${style}-${suffix}, var(--advanced-camera-card-menu-position-${position}-alignment-${alignment}-${suffix}, var(--advanced-camera-card-menu-position-${position}-${suffix}, var(--advanced-camera-card-menu-style-${style}-${suffix}, var(--advanced-camera-card-menu-alignment-${alignment}-${suffix}, var(--advanced-camera-card-menu-${suffix})))))))`; }; // By definition `rule` will match the current configuration, the choice is // actually which of the var(...) variables will be used after the match. const expandedRule = style === 'hidden' ? '[expanded]' : ''; const rule = `[data-position='${position}']` + `[data-style='${style}']` + `[data-alignment='${alignment}']` + expandedRule; return html``; } protected render(): TemplateResult | void { const config = this._controller.getMenuConfig(); const style = config?.style; if (!config || style === 'none') { return; } const matchingButtons = this._controller.getButtons('matching'); const opposingButtons = this._controller.getButtons('opposing'); return html` ${this._renderPerInstanceStyle()}
${matchingButtons.map((button) => this._renderButton(button))}
${opposingButtons.map((button) => this._renderButton(button))}
`; } static get styles(): CSSResultGroup { return unsafeCSS(menuStyle); } } declare global { interface HTMLElementTagNameMap { 'advanced-camera-card-menu': AdvancedCameraCardMenu; } }