mirror of
				https://github.com/home-assistant/frontend.git
				synced 2025-10-31 06:29:43 +00:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			dropdown
			...
			button-hea
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 885a590443 | 
| @@ -1,5 +0,0 @@ | ||||
| --- | ||||
| title: Dropdown | ||||
| --- | ||||
|  | ||||
| # Dropdown `<ha-dropdown>` | ||||
| @@ -1,133 +0,0 @@ | ||||
| import { | ||||
|   mdiContentCopy, | ||||
|   mdiContentCut, | ||||
|   mdiContentPaste, | ||||
|   mdiDelete, | ||||
| } from "@mdi/js"; | ||||
| import type { TemplateResult } from "lit"; | ||||
| import { css, html, LitElement } from "lit"; | ||||
| import { customElement } from "lit/decorators"; | ||||
| import "../../../../src/components/ha-button"; | ||||
| import "../../../../src/components/ha-card"; | ||||
| import "../../../../src/components/ha-svg-icon"; | ||||
| import "../../../../src/components/ha-dropdown-item"; | ||||
| import "@home-assistant/webawesome/dist/components/icon/icon"; | ||||
| import "@home-assistant/webawesome/dist/components/button/button"; | ||||
| import "@home-assistant/webawesome/dist/components/dropdown/dropdown"; | ||||
| import "../../../../src/components/ha-dropdown"; | ||||
| import "@home-assistant/webawesome/dist/components/popup/popup"; | ||||
| import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element"; | ||||
| import "../../../../src/components/ha-icon-button"; | ||||
|  | ||||
| @customElement("demo-components-ha-dropdown") | ||||
| export class DemoHaDropdown extends LitElement { | ||||
|   protected render(): TemplateResult { | ||||
|     return html` | ||||
|       ${["light", "dark"].map( | ||||
|         (mode) => html` | ||||
|           <div class=${mode}> | ||||
|             <ha-card header="ha-button in ${mode}"> | ||||
|               <div class="card-content"> | ||||
|                 <ha-dropdown open> | ||||
|                   <ha-button slot="trigger" with-caret>Dropdown</ha-button> | ||||
|  | ||||
|                   <ha-dropdown-item> | ||||
|                     <ha-svg-icon | ||||
|                       .path=${mdiContentCut} | ||||
|                       slot="icon" | ||||
|                     ></ha-svg-icon> | ||||
|                     Cut | ||||
|                   </ha-dropdown-item> | ||||
|                   <ha-dropdown-item> | ||||
|                     <ha-svg-icon | ||||
|                       .path=${mdiContentCopy} | ||||
|                       slot="icon" | ||||
|                     ></ha-svg-icon> | ||||
|                     Copy | ||||
|                   </ha-dropdown-item> | ||||
|                   <ha-dropdown-item disabled> | ||||
|                     <ha-svg-icon | ||||
|                       .path=${mdiContentPaste} | ||||
|                       slot="icon" | ||||
|                     ></ha-svg-icon> | ||||
|                     Paste | ||||
|                   </ha-dropdown-item> | ||||
|                   <ha-dropdown-item> | ||||
|                     Show images | ||||
|                     <ha-dropdown-item slot="submenu" value="show-all-images" | ||||
|                       >Show All Images</ha-dropdown-item | ||||
|                     > | ||||
|                     <ha-dropdown-item slot="submenu" value="show-thumbnails" | ||||
|                       >Show Thumbnails</ha-dropdown-item | ||||
|                     > | ||||
|                   </ha-dropdown-item> | ||||
|                   <ha-dropdown-item type="checkbox" checked | ||||
|                     >Emoji Shortcuts</ha-dropdown-item | ||||
|                   > | ||||
|                   <ha-dropdown-item type="checkbox" checked | ||||
|                     >Word Wrap</ha-dropdown-item | ||||
|                   > | ||||
|                   <ha-dropdown-item variant="danger"> | ||||
|                     <ha-svg-icon .path=${mdiDelete} slot="icon"></ha-svg-icon> | ||||
|                     Delete | ||||
|                   </ha-dropdown-item> | ||||
|                 </ha-dropdown> | ||||
|               </div> | ||||
|             </ha-card> | ||||
|           </div> | ||||
|         ` | ||||
|       )} | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   firstUpdated(changedProps) { | ||||
|     super.firstUpdated(changedProps); | ||||
|     applyThemesOnElement( | ||||
|       this.shadowRoot!.querySelector(".dark"), | ||||
|       { | ||||
|         default_theme: "default", | ||||
|         default_dark_theme: "default", | ||||
|         themes: {}, | ||||
|         darkMode: true, | ||||
|         theme: "default", | ||||
|       }, | ||||
|       undefined, | ||||
|       undefined, | ||||
|       true | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   static styles = css` | ||||
|     :host { | ||||
|       display: flex; | ||||
|       justify-content: center; | ||||
|     } | ||||
|     .dark, | ||||
|     .light { | ||||
|       display: block; | ||||
|       background-color: var(--primary-background-color); | ||||
|       padding: 0 50px; | ||||
|     } | ||||
|     .button { | ||||
|       padding: unset; | ||||
|     } | ||||
|     ha-card { | ||||
|       margin: 24px auto; | ||||
|     } | ||||
|     .card-content { | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       gap: 24px; | ||||
|     } | ||||
|     .card-content div { | ||||
|       display: flex; | ||||
|       gap: 8px; | ||||
|     } | ||||
|   `; | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   interface HTMLElementTagNameMap { | ||||
|     "demo-components-ha-dropdown": DemoHaDropdown; | ||||
|   } | ||||
| } | ||||
| @@ -34,7 +34,7 @@ | ||||
|     "@codemirror/legacy-modes": "6.5.2", | ||||
|     "@codemirror/search": "6.5.11", | ||||
|     "@codemirror/state": "6.5.2", | ||||
|     "@codemirror/view": "6.38.5", | ||||
|     "@codemirror/view": "6.38.4", | ||||
|     "@date-fns/tz": "1.4.1", | ||||
|     "@egjs/hammerjs": "2.0.17", | ||||
|     "@formatjs/intl-datetimeformat": "6.18.1", | ||||
| @@ -122,7 +122,7 @@ | ||||
|     "lit": "3.3.1", | ||||
|     "lit-html": "3.3.1", | ||||
|     "luxon": "3.7.2", | ||||
|     "marked": "16.4.0", | ||||
|     "marked": "16.3.0", | ||||
|     "memoize-one": "6.0.0", | ||||
|     "node-vibrant": "4.0.3", | ||||
|     "object-hash": "3.0.0", | ||||
|   | ||||
| @@ -1,23 +0,0 @@ | ||||
| import DropdownItem from "@home-assistant/webawesome/dist/components/dropdown-item/dropdown-item"; | ||||
| import { css, type CSSResultGroup } from "lit"; | ||||
| import { customElement } from "lit/decorators"; | ||||
|  | ||||
| @customElement("ha-dropdown-item") | ||||
| export class HaDropdownItem extends DropdownItem { | ||||
|   static get styles(): CSSResultGroup { | ||||
|     return [ | ||||
|       DropdownItem.styles, | ||||
|       css` | ||||
|         :host { | ||||
|           min-height: 40px; | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   interface HTMLElementTagNameMap { | ||||
|     "ha-dropdown-item": HaDropdownItem; | ||||
|   } | ||||
| } | ||||
| @@ -1,35 +0,0 @@ | ||||
| import Dropdown from "@home-assistant/webawesome/dist/components/dropdown/dropdown"; | ||||
| import { css, type CSSResultGroup } from "lit"; | ||||
| import { customElement, property } from "lit/decorators"; | ||||
|  | ||||
| @customElement("ha-dropdown") | ||||
| export class HaDropdown extends Dropdown { | ||||
|   @property({ attribute: false }) dropdownTag = "ha-dropdown"; | ||||
|  | ||||
|   @property({ attribute: false }) dropdownItemTag = "ha-dropdown-item"; | ||||
|  | ||||
|   static get styles(): CSSResultGroup { | ||||
|     return [ | ||||
|       Dropdown.styles, | ||||
|       css` | ||||
|         :host { | ||||
|           --wa-color-surface-border: var(--ha-color-border-normal); | ||||
|           --wa-color-surface-raised: var( | ||||
|             --card-background-color, | ||||
|             var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff)), | ||||
|           ); | ||||
|         } | ||||
|  | ||||
|         #menu { | ||||
|           --wa-shadow-m: 0px 4px 8px 0px var(--ha-color-shadow); | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   interface HTMLElementTagNameMap { | ||||
|     "ha-dropdown": HaDropdown; | ||||
|   } | ||||
| } | ||||
| @@ -167,12 +167,15 @@ class MoreInfoMediaPlayer extends LitElement { | ||||
|     } | ||||
|  | ||||
|     return html`<ha-md-button-menu positioning="popover"> | ||||
|       <ha-icon-button | ||||
|       <ha-button | ||||
|         slot="trigger" | ||||
|         .title=${this.hass.localize(`ui.card.media_player.source`)} | ||||
|         .path=${mdiLoginVariant} | ||||
|         appearance="plain" | ||||
|         variant="neutral" | ||||
|         size="small" | ||||
|         title=${this.hass.localize(`ui.card.media_player.source`)} | ||||
|       > | ||||
|       </ha-icon-button> | ||||
|         <ha-svg-icon .path=${mdiLoginVariant}></ha-svg-icon> | ||||
|       </ha-button> | ||||
|       ${this.stateObj.attributes.source_list!.map( | ||||
|         (source) => | ||||
|           html`<ha-md-menu-item | ||||
| @@ -200,12 +203,15 @@ class MoreInfoMediaPlayer extends LitElement { | ||||
|     } | ||||
|  | ||||
|     return html`<ha-md-button-menu positioning="popover"> | ||||
|       <ha-icon-button | ||||
|       <ha-button | ||||
|         slot="trigger" | ||||
|         .title=${this.hass.localize(`ui.card.media_player.sound_mode`)} | ||||
|         .path=${mdiMusicNoteEighth} | ||||
|         appearance="plain" | ||||
|         variant="neutral" | ||||
|         size="small" | ||||
|         title=${this.hass.localize(`ui.card.media_player.sound_mode`)} | ||||
|       > | ||||
|       </ha-icon-button> | ||||
|         <ha-svg-icon .path=${mdiMusicNoteEighth}></ha-svg-icon> | ||||
|       </ha-button> | ||||
|       ${this.stateObj.attributes.sound_mode_list!.map( | ||||
|         (soundMode) => | ||||
|           html`<ha-md-menu-item | ||||
| @@ -231,17 +237,21 @@ class MoreInfoMediaPlayer extends LitElement { | ||||
|     const groupMembers = this.stateObj.attributes.group_members; | ||||
|     const hasMultipleMembers = groupMembers && groupMembers?.length > 1; | ||||
|  | ||||
|     return html`<ha-icon-button | ||||
|     return html`<ha-button | ||||
|       class="grouping" | ||||
|       @click=${this._showGroupMediaPlayers} | ||||
|       .title=${this.hass.localize("ui.card.media_player.join")} | ||||
|       appearance="plain" | ||||
|       variant="neutral" | ||||
|       size="small" | ||||
|       title=${this.hass.localize("ui.card.media_player.join")} | ||||
|     > | ||||
|       <div class="grouping"> | ||||
|       <div> | ||||
|         <ha-svg-icon .path=${mdiSpeakerMultiple}></ha-svg-icon> | ||||
|         ${hasMultipleMembers | ||||
|           ? html`<span class="badge"> ${groupMembers?.length || 4} </span>` | ||||
|           : nothing} | ||||
|       </div> | ||||
|     </ha-icon-button>`; | ||||
|     </ha-button>`; | ||||
|   } | ||||
|  | ||||
|   protected _renderEmptyCover(title: string, icon?: string) { | ||||
| @@ -404,39 +414,48 @@ class MoreInfoMediaPlayer extends LitElement { | ||||
|           ${!isUnavailableState(stateObj.state) && | ||||
|           supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA) | ||||
|             ? html` | ||||
|                 <ha-icon-button | ||||
|                 <ha-button | ||||
|                   @click=${this._showBrowseMedia} | ||||
|                   .title=${this.hass.localize( | ||||
|                   appearance="plain" | ||||
|                   variant="neutral" | ||||
|                   size="small" | ||||
|                   title=${this.hass.localize( | ||||
|                     "ui.card.media_player.browse_media" | ||||
|                   )} | ||||
|                   .path=${mdiPlayBoxMultiple} | ||||
|                 > | ||||
|                 </ha-icon-button> | ||||
|                   <ha-svg-icon .path=${mdiPlayBoxMultiple}></ha-svg-icon> | ||||
|                 </ha-button> | ||||
|               ` | ||||
|             : nothing} | ||||
|           ${this._renderGrouping()} ${this._renderSourceControl()} | ||||
|           ${this._renderSoundMode()} | ||||
|           ${turnOn | ||||
|             ? html`<ha-icon-button | ||||
|             ? html`<ha-button | ||||
|                 action=${turnOn.action} | ||||
|                 @click=${this._handleClick} | ||||
|                 .title=${this.hass.localize( | ||||
|                 appearance="plain" | ||||
|                 variant="neutral" | ||||
|                 size="small" | ||||
|                 title=${this.hass.localize( | ||||
|                   `ui.card.media_player.${turnOn.action}` | ||||
|                 )} | ||||
|                 .path=${turnOn.icon} | ||||
|               > | ||||
|               </ha-icon-button>` | ||||
|                 <ha-svg-icon .path=${turnOn.icon}></ha-svg-icon> | ||||
|               </ha-button>` | ||||
|             : nothing} | ||||
|           ${turnOff | ||||
|             ? html`<ha-icon-button | ||||
|             ? html`<ha-button | ||||
|                 action=${turnOff.action} | ||||
|                 @click=${this._handleClick} | ||||
|                 .title=${this.hass.localize( | ||||
|                 appearance="plain" | ||||
|                 variant="neutral" | ||||
|                 size="small" | ||||
|                 title=${this.hass.localize( | ||||
|                   `ui.card.media_player.${turnOff.action}` | ||||
|                 )} | ||||
|                 .path=${turnOff.icon} | ||||
|               > | ||||
|               </ha-icon-button>` | ||||
|                 <ha-svg-icon .path=${turnOff.icon}></ha-svg-icon> | ||||
|               </ha-button>` | ||||
|             : nothing} | ||||
|         </div> | ||||
|       </div> | ||||
| @@ -560,7 +579,7 @@ class MoreInfoMediaPlayer extends LitElement { | ||||
|       font-size: var(--ha-font-size-xs); | ||||
|       background-color: var(--primary-color); | ||||
|       padding: 0 4px; | ||||
|       color: var(--text-primary-color); | ||||
|       color: var(--primary-text-color); | ||||
|     } | ||||
|  | ||||
|     .position-bar { | ||||
| @@ -597,15 +616,15 @@ class MoreInfoMediaPlayer extends LitElement { | ||||
|       justify-content: space-around; | ||||
|     } | ||||
|  | ||||
|     .controls-row ha-icon-button { | ||||
|       color: var(--secondary-text-color); | ||||
|     .controls-row ha-button { | ||||
|       width: 32px; | ||||
|     } | ||||
|  | ||||
|     .controls-row ha-svg-icon { | ||||
|       color: var(--ha-color-on-neutral-quiet); | ||||
|     } | ||||
|  | ||||
|     .grouping { | ||||
|     .grouping::part(label) { | ||||
|       position: relative; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import "../heading-badges/hui-entity-heading-badge"; | ||||
| import "../heading-badges/hui-button-heading-badge"; | ||||
|  | ||||
| import { | ||||
|   createLovelaceElement, | ||||
| @@ -6,7 +7,7 @@ import { | ||||
| } from "./create-element-base"; | ||||
| import type { LovelaceHeadingBadgeConfig } from "../heading-badges/types"; | ||||
|  | ||||
| const ALWAYS_LOADED_TYPES = new Set(["error", "entity"]); | ||||
| const ALWAYS_LOADED_TYPES = new Set(["error", "entity", "button"]); | ||||
|  | ||||
| export const createHeadingBadgeElement = (config: LovelaceHeadingBadgeConfig) => | ||||
|   createLovelaceElement( | ||||
|   | ||||
| @@ -0,0 +1,238 @@ | ||||
| import { mdiEye, mdiGestureTap, mdiTextShort } from "@mdi/js"; | ||||
| import { css, html, LitElement, nothing } from "lit"; | ||||
| import { customElement, property, state } from "lit/decorators"; | ||||
| import memoizeOne from "memoize-one"; | ||||
| import { any, array, assert, object, optional, string } from "superstruct"; | ||||
| import { fireEvent } from "../../../../common/dom/fire_event"; | ||||
| import "../../../../components/ha-expansion-panel"; | ||||
| import "../../../../components/ha-form/ha-form"; | ||||
| import type { | ||||
|   HaFormSchema, | ||||
|   SchemaUnion, | ||||
| } from "../../../../components/ha-form/types"; | ||||
| import type { HomeAssistant } from "../../../../types"; | ||||
| import type { Condition } from "../../common/validate-condition"; | ||||
| import type { UiAction } from "../../components/hui-action-editor"; | ||||
| import type { ButtonHeadingBadgeConfig } from "../../heading-badges/types"; | ||||
| import type { LovelaceGenericElementEditor } from "../../types"; | ||||
| import "../conditions/ha-card-conditions-editor"; | ||||
| import { configElementStyle } from "../config-elements/config-elements-style"; | ||||
| import { actionConfigStruct } from "../structs/action-struct"; | ||||
|  | ||||
| export const DEFAULT_CONFIG: Partial<ButtonHeadingBadgeConfig> = { | ||||
|   type: "button", | ||||
| }; | ||||
|  | ||||
| const entityConfigStruct = object({ | ||||
|   type: optional(string()), | ||||
|   text: optional(string()), | ||||
|   icon: optional(string()), | ||||
|   tap_action: optional(actionConfigStruct), | ||||
|   hold_action: optional(actionConfigStruct), | ||||
|   double_tap_action: optional(actionConfigStruct), | ||||
|   visibility: optional(array(any())), | ||||
| }); | ||||
|  | ||||
| const ALLOWED_ACTIONS: UiAction[] = [ | ||||
|   "navigate", | ||||
|   "url", | ||||
|   "assist", | ||||
|   "call-service", | ||||
|   "none", | ||||
| ]; | ||||
|  | ||||
| @customElement("hui-heading-button-editor") | ||||
| export class HuiHeadingButtonEditor | ||||
|   extends LitElement | ||||
|   implements LovelaceGenericElementEditor | ||||
| { | ||||
|   @property({ attribute: false }) public hass?: HomeAssistant; | ||||
|  | ||||
|   @property({ type: Boolean }) public preview = false; | ||||
|  | ||||
|   @state() private _config?: ButtonHeadingBadgeConfig; | ||||
|  | ||||
|   public setConfig(config: ButtonHeadingBadgeConfig): void { | ||||
|     assert(config, entityConfigStruct); | ||||
|     this._config = { | ||||
|       ...DEFAULT_CONFIG, | ||||
|       ...config, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   private _schema = memoizeOne( | ||||
|     () => | ||||
|       [ | ||||
|         { | ||||
|           name: "content", | ||||
|           type: "expandable", | ||||
|           flatten: true, | ||||
|           iconPath: mdiTextShort, | ||||
|           schema: [ | ||||
|             { | ||||
|               name: "", | ||||
|               type: "grid", | ||||
|               schema: [ | ||||
|                 { | ||||
|                   name: "text", | ||||
|                   selector: { | ||||
|                     text: {}, | ||||
|                   }, | ||||
|                 }, | ||||
|                 { | ||||
|                   name: "icon", | ||||
|                   selector: { icon: {} }, | ||||
|                   context: { icon_entity: "entity" }, | ||||
|                 }, | ||||
|               ], | ||||
|             }, | ||||
|           ], | ||||
|         }, | ||||
|         { | ||||
|           name: "interactions", | ||||
|           type: "expandable", | ||||
|           flatten: true, | ||||
|           iconPath: mdiGestureTap, | ||||
|           schema: [ | ||||
|             { | ||||
|               name: "tap_action", | ||||
|               selector: { | ||||
|                 ui_action: { | ||||
|                   default_action: "none", | ||||
|                   actions: ALLOWED_ACTIONS, | ||||
|                 }, | ||||
|               }, | ||||
|             }, | ||||
|             { | ||||
|               name: "", | ||||
|               type: "optional_actions", | ||||
|               flatten: true, | ||||
|               schema: (["hold_action", "double_tap_action"] as const).map( | ||||
|                 (action) => ({ | ||||
|                   name: action, | ||||
|                   selector: { | ||||
|                     ui_action: { | ||||
|                       default_action: "none" as const, | ||||
|                       actions: ALLOWED_ACTIONS, | ||||
|                     }, | ||||
|                   }, | ||||
|                 }) | ||||
|               ), | ||||
|             }, | ||||
|           ], | ||||
|         }, | ||||
|       ] as const satisfies readonly HaFormSchema[] | ||||
|   ); | ||||
|  | ||||
|   protected render() { | ||||
|     if (!this.hass || !this._config) { | ||||
|       return nothing; | ||||
|     } | ||||
|  | ||||
|     const schema = this._schema(); | ||||
|  | ||||
|     const conditions = this._config.visibility ?? []; | ||||
|     return html` | ||||
|       <ha-form | ||||
|         .hass=${this.hass} | ||||
|         .data=${this._config} | ||||
|         .schema=${schema} | ||||
|         .computeLabel=${this._computeLabelCallback} | ||||
|         @value-changed=${this._valueChanged} | ||||
|       ></ha-form> | ||||
|       <ha-expansion-panel outlined> | ||||
|         <ha-svg-icon slot="leading-icon" .path=${mdiEye}></ha-svg-icon> | ||||
|         <h3 slot="header"> | ||||
|           ${this.hass!.localize( | ||||
|             "ui.panel.lovelace.editor.card.heading.entity_config.visibility" | ||||
|           )} | ||||
|         </h3> | ||||
|         <div class="content"> | ||||
|           <p class="intro"> | ||||
|             ${this.hass.localize( | ||||
|               "ui.panel.lovelace.editor.card.heading.entity_config.visibility_explanation" | ||||
|             )} | ||||
|           </p> | ||||
|           <ha-card-conditions-editor | ||||
|             .hass=${this.hass} | ||||
|             .conditions=${conditions} | ||||
|             @value-changed=${this._conditionChanged} | ||||
|           > | ||||
|           </ha-card-conditions-editor> | ||||
|         </div> | ||||
|       </ha-expansion-panel> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   private _valueChanged(ev: CustomEvent): void { | ||||
|     ev.stopPropagation(); | ||||
|     if (!this._config || !this.hass) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const config = { ...ev.detail.value } as FormData; | ||||
|  | ||||
|     fireEvent(this, "config-changed", { config }); | ||||
|   } | ||||
|  | ||||
|   private _conditionChanged(ev: CustomEvent): void { | ||||
|     ev.stopPropagation(); | ||||
|     if (!this._config || !this.hass) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const conditions = ev.detail.value as Condition[]; | ||||
|  | ||||
|     const newConfig: ButtonHeadingBadgeConfig = { | ||||
|       ...this._config, | ||||
|       visibility: conditions, | ||||
|     }; | ||||
|     if (newConfig.visibility?.length === 0) { | ||||
|       delete newConfig.visibility; | ||||
|     } | ||||
|  | ||||
|     fireEvent(this, "config-changed", { config: newConfig }); | ||||
|   } | ||||
|  | ||||
|   private _computeLabelCallback = ( | ||||
|     schema: SchemaUnion<ReturnType<typeof this._schema>> | ||||
|   ) => { | ||||
|     switch (schema.name) { | ||||
|       case "text": | ||||
|         return this.hass!.localize( | ||||
|           `ui.panel.lovelace.editor.card.heading.button_config.${schema.name}` | ||||
|         ); | ||||
|       default: | ||||
|         return this.hass!.localize( | ||||
|           `ui.panel.lovelace.editor.card.generic.${schema.name}` | ||||
|         ); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   static get styles() { | ||||
|     return [ | ||||
|       configElementStyle, | ||||
|       css` | ||||
|         .container { | ||||
|           display: flex; | ||||
|           flex-direction: column; | ||||
|         } | ||||
|         ha-form { | ||||
|           display: block; | ||||
|           margin-bottom: 24px; | ||||
|         } | ||||
|         .intro { | ||||
|           margin: 0; | ||||
|           color: var(--secondary-text-color); | ||||
|           margin-bottom: 8px; | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   interface HTMLElementTagNameMap { | ||||
|     "hui-heading-button-editor": HuiHeadingButtonEditor; | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,96 @@ | ||||
| import { LitElement, css, html, nothing } from "lit"; | ||||
| import { customElement, property, state } from "lit/decorators"; | ||||
| import "../../../components/ha-state-icon"; | ||||
| import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler"; | ||||
| import "../../../state-display/state-display"; | ||||
| import type { HomeAssistant } from "../../../types"; | ||||
| import { actionHandler } from "../common/directives/action-handler-directive"; | ||||
| import { handleAction } from "../common/handle-action"; | ||||
| import { hasAction } from "../common/has-action"; | ||||
| import { DEFAULT_CONFIG } from "../editor/heading-badge-editor/hui-entity-heading-badge-editor"; | ||||
| import type { | ||||
|   LovelaceHeadingBadge, | ||||
|   LovelaceHeadingBadgeEditor, | ||||
| } from "../types"; | ||||
| import type { ButtonHeadingBadgeConfig } from "./types"; | ||||
|  | ||||
| const DEFAULT_ACTIONS: Pick< | ||||
|   ButtonHeadingBadgeConfig, | ||||
|   "tap_action" | "hold_action" | "double_tap_action" | ||||
| > = { | ||||
|   tap_action: { action: "none" }, | ||||
|   hold_action: { action: "none" }, | ||||
|   double_tap_action: { action: "none" }, | ||||
| }; | ||||
|  | ||||
| @customElement("hui-button-heading-badge") | ||||
| export class HuiButtonHeadingBadge | ||||
|   extends LitElement | ||||
|   implements LovelaceHeadingBadge | ||||
| { | ||||
|   public static async getConfigElement(): Promise<LovelaceHeadingBadgeEditor> { | ||||
|     await import( | ||||
|       "../editor/heading-badge-editor/hui-button-heading-badge-editor" | ||||
|     ); | ||||
|     return document.createElement("hui-heading-button-editor"); | ||||
|   } | ||||
|  | ||||
|   @property({ attribute: false }) public hass?: HomeAssistant; | ||||
|  | ||||
|   @state() private _config?: ButtonHeadingBadgeConfig; | ||||
|  | ||||
|   @property({ type: Boolean }) public preview = false; | ||||
|  | ||||
|   public setConfig(config): void { | ||||
|     this._config = { | ||||
|       ...DEFAULT_CONFIG, | ||||
|       ...DEFAULT_ACTIONS, | ||||
|       ...config, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   get hasAction() { | ||||
|     return ( | ||||
|       hasAction(this._config?.tap_action) || | ||||
|       hasAction(this._config?.hold_action) || | ||||
|       hasAction(this._config?.double_tap_action) | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   private _handleAction(ev: ActionHandlerEvent) { | ||||
|     handleAction(this, this.hass!, this._config!, ev.detail.action!); | ||||
|   } | ||||
|  | ||||
|   protected render() { | ||||
|     if (!this.hass || !this._config) { | ||||
|       return nothing; | ||||
|     } | ||||
|  | ||||
|     const config = this._config; | ||||
|  | ||||
|     return html` | ||||
|       <ha-button | ||||
|         @action=${this._handleAction} | ||||
|         .actionHandler=${actionHandler({ | ||||
|           hasHold: hasAction(this._config!.hold_action), | ||||
|           hasDoubleClick: hasAction(this._config!.double_tap_action), | ||||
|         })} | ||||
|       > | ||||
|         <ha-icon .icon=${config.icon}></ha-icon> | ||||
|         ${this._config.text} | ||||
|       </ha-button> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   static styles = css` | ||||
|     [role="button"] { | ||||
|       cursor: pointer; | ||||
|     } | ||||
|   `; | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   interface HTMLElementTagNameMap { | ||||
|     "hui-button-heading-badge": HuiButtonHeadingBadge; | ||||
|   } | ||||
| } | ||||
| @@ -26,3 +26,12 @@ export interface EntityHeadingBadgeConfig extends LovelaceHeadingBadgeConfig { | ||||
|   hold_action?: ActionConfig; | ||||
|   double_tap_action?: ActionConfig; | ||||
| } | ||||
|  | ||||
| export interface ButtonHeadingBadgeConfig extends LovelaceHeadingBadgeConfig { | ||||
|   type?: "button"; | ||||
|   icon: string; | ||||
|   text?: string; | ||||
|   tap_action: ActionConfig; | ||||
|   hold_action?: ActionConfig; | ||||
|   double_tap_action?: ActionConfig; | ||||
| } | ||||
|   | ||||
| @@ -29,8 +29,6 @@ import { | ||||
| } from "../../dialogs/generic/show-dialog-box"; | ||||
| import { haStyle } from "../../resources/styles"; | ||||
| import type { HomeAssistant } from "../../types"; | ||||
| import "../../components/ha-dropdown-item"; | ||||
| import "../../components/ha-dropdown"; | ||||
|  | ||||
| // Client ID used by iOS app | ||||
| const iOSclientId = "https://home-assistant.io/iOS"; | ||||
| @@ -148,18 +146,19 @@ class HaRefreshTokens extends LitElement { | ||||
|                           )} | ||||
|                     </div> | ||||
|                     <div> | ||||
|                       <ha-dropdown> | ||||
|                       <ha-md-button-menu positioning="popover"> | ||||
|                         <ha-icon-button | ||||
|                           slot="trigger" | ||||
|                           .label=${this.hass.localize("ui.common.menu")} | ||||
|                           .path=${mdiDotsVertical} | ||||
|                         ></ha-icon-button> | ||||
|                         <ha-dropdown-item | ||||
|                         <ha-md-menu-item | ||||
|                           graphic="icon" | ||||
|                           @click=${this._toggleTokenExpiration} | ||||
|                           .token=${token} | ||||
|                         > | ||||
|                           <ha-svg-icon | ||||
|                             slot="icon" | ||||
|                             slot="start" | ||||
|                             .path=${token.expire_at | ||||
|                               ? mdiClockRemoveOutline | ||||
|                               : mdiClockCheckOutline} | ||||
| @@ -171,20 +170,24 @@ class HaRefreshTokens extends LitElement { | ||||
|                             : this.hass.localize( | ||||
|                                 "ui.panel.profile.refresh_tokens.enable_token_expiration" | ||||
|                               )} | ||||
|                         </ha-dropdown-item> | ||||
|                         <ha-dropdown-item | ||||
|                           variant="danger" | ||||
|                         </ha-md-menu-item> | ||||
|                         <ha-md-menu-item | ||||
|                           graphic="icon" | ||||
|                           class="warning" | ||||
|                           .disabled=${token.is_current} | ||||
|                           @click=${this._deleteToken} | ||||
|                           .token=${token} | ||||
|                         > | ||||
|                           <ha-svg-icon | ||||
|                             slot="icon" | ||||
|                             class="warning" | ||||
|                             slot="start" | ||||
|                             .path=${mdiDelete} | ||||
|                           ></ha-svg-icon> | ||||
|                           <div slot="headline"> | ||||
|                             ${this.hass.localize("ui.common.delete")} | ||||
|                         </ha-dropdown-item> | ||||
|                       </ha-dropdown> | ||||
|                           </div> | ||||
|                         </ha-md-menu-item> | ||||
|                       </ha-md-button-menu> | ||||
|                     </div> | ||||
|                   </ha-settings-row> | ||||
|                 ` | ||||
|   | ||||
| @@ -156,9 +156,6 @@ export const semanticColorStyles = css` | ||||
|     /* Surfaces */ | ||||
|     --ha-color-surface-default: var(--ha-color-neutral-95); | ||||
|     --ha-color-on-surface-default: var(--ha-color-neutral-05); | ||||
|      | ||||
|     /* shadow */ | ||||
|     --ha-color-shadow: rgba(0, 0, 0, 0.2); | ||||
|   } | ||||
| `; | ||||
|  | ||||
| @@ -291,8 +288,5 @@ export const darkSemanticColorStyles = css` | ||||
|     /* Surfaces */ | ||||
|     --ha-color-surface-default: var(--ha-color-neutral-10); | ||||
|     --ha-color-on-surface-default: var(--ha-color-neutral-95); | ||||
|  | ||||
|     /* shadow */ | ||||
|     --ha-color-shadow: rgba(255, 255, 255, 0.2); | ||||
|   } | ||||
| `; | ||||
|   | ||||
| @@ -52,11 +52,6 @@ export const waColorStyles = css` | ||||
|     --wa-color-danger-on-normal: var(--ha-color-on-danger-normal); | ||||
|     --wa-color-danger-on-quiet: var(--ha-color-on-danger-quiet); | ||||
|  | ||||
|     --wa-color-text-normal: var(--ha-color-text-primary); | ||||
|     --wa-color-surface-default: var(--card-background-color); | ||||
|     --wa-color-surface-raised: var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff)); | ||||
|     --wa-color-surface-border: var(--ha-color-border-normal); | ||||
|  | ||||
|     --wa-focus-ring-color: var(--ha-color-neutral-60); | ||||
|   } | ||||
| `; | ||||
|   | ||||
| @@ -16,16 +16,7 @@ export const waMainStyles = css` | ||||
|     --wa-font-weight-action: var(--ha-font-weight-medium); | ||||
|     --wa-transition-fast: 75ms; | ||||
|     --wa-transition-easing: ease; | ||||
|     --wa-border-style: solid; | ||||
|     --wa-border-width-s: var(--ha-border-width-sm); | ||||
|     --wa-border-width-m: var(--ha-border-width-md); | ||||
|     --wa-border-width-l: var(--ha-border-width-lg); | ||||
|     --wa-border-radius-s: var(--ha-border-radius-sm); | ||||
|     --wa-border-radius-m: var(--ha-border-radius-md); | ||||
|     --wa-border-radius-l: var(--ha-border-radius-lg); | ||||
|  | ||||
|     --wa-line-height-condensed: 1.25; | ||||
|  | ||||
|     --wa-border-width-l: var(--ha-border-radius-l); | ||||
|     --wa-space-xl: 32px; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -7766,6 +7766,9 @@ | ||||
|                   "state": "[%key:ui::panel::lovelace::editor::badge::entity::displayed_elements_options::state%]" | ||||
|                 } | ||||
|               }, | ||||
|               "button_config": { | ||||
|                 "text": "Text" | ||||
|               }, | ||||
|               "default_heading": "Kitchen" | ||||
|             }, | ||||
|             "map": { | ||||
| @@ -7936,7 +7939,7 @@ | ||||
|               "hide_completed": "Hide completed items", | ||||
|               "hide_create": "Hide 'Add item' field", | ||||
|               "hide_section_headers": "Hide section headers", | ||||
|               "hide_section_headers_helper": "Removes the 'Active' and 'Completed' section headers and their overflow menus.", | ||||
|               "hide_section_headers_helper": "Removes the 'Active' and 'Completed' sections with the overflow menus.", | ||||
|               "display_order": "Display order", | ||||
|               "item_tap_action": "Item tap behavior", | ||||
|               "actions": { | ||||
|   | ||||
							
								
								
									
										20
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								yarn.lock
									
									
									
									
									
								
							| @@ -1284,15 +1284,15 @@ __metadata: | ||||
|   languageName: node | ||||
|   linkType: hard | ||||
|  | ||||
| "@codemirror/view@npm:6.38.5, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0": | ||||
|   version: 6.38.5 | ||||
|   resolution: "@codemirror/view@npm:6.38.5" | ||||
| "@codemirror/view@npm:6.38.4, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0": | ||||
|   version: 6.38.4 | ||||
|   resolution: "@codemirror/view@npm:6.38.4" | ||||
|   dependencies: | ||||
|     "@codemirror/state": "npm:^6.5.0" | ||||
|     crelt: "npm:^1.0.6" | ||||
|     style-mod: "npm:^4.1.0" | ||||
|     w3c-keyname: "npm:^2.2.4" | ||||
|   checksum: 10/2335b593770042eb3adfe369073432b07cd2d15f1e230ae4dc7be7a7b8edd74e57c13e59b92a11e7e5d59ae030aabf7f55478dfec1cf2a2fe3a1ef3f091676a4 | ||||
|   checksum: 10/86b3894e9e7c2113aabb1db8684d0520378339c194fa56a688fc26cd7d40336bb9df1f5f19f68309d95f14b80ecf0b70c0ffe5e43f2ec11c4bab18f2d5ee4494 | ||||
|   languageName: node | ||||
|   linkType: hard | ||||
|  | ||||
| @@ -9189,7 +9189,7 @@ __metadata: | ||||
|     "@codemirror/legacy-modes": "npm:6.5.2" | ||||
|     "@codemirror/search": "npm:6.5.11" | ||||
|     "@codemirror/state": "npm:6.5.2" | ||||
|     "@codemirror/view": "npm:6.38.5" | ||||
|     "@codemirror/view": "npm:6.38.4" | ||||
|     "@date-fns/tz": "npm:1.4.1" | ||||
|     "@egjs/hammerjs": "npm:2.0.17" | ||||
|     "@formatjs/intl-datetimeformat": "npm:6.18.1" | ||||
| @@ -9332,7 +9332,7 @@ __metadata: | ||||
|     lodash.template: "npm:4.5.0" | ||||
|     luxon: "npm:3.7.2" | ||||
|     map-stream: "npm:0.0.7" | ||||
|     marked: "npm:16.4.0" | ||||
|     marked: "npm:16.3.0" | ||||
|     memoize-one: "npm:6.0.0" | ||||
|     node-vibrant: "npm:4.0.3" | ||||
|     object-hash: "npm:3.0.0" | ||||
| @@ -10974,12 +10974,12 @@ __metadata: | ||||
|   languageName: node | ||||
|   linkType: hard | ||||
|  | ||||
| "marked@npm:16.4.0": | ||||
|   version: 16.4.0 | ||||
|   resolution: "marked@npm:16.4.0" | ||||
| "marked@npm:16.3.0": | ||||
|   version: 16.3.0 | ||||
|   resolution: "marked@npm:16.3.0" | ||||
|   bin: | ||||
|     marked: bin/marked.js | ||||
|   checksum: 10/5174b345ccc61e2030c2eb8abb3e5cbebeb6697a6d2b609f64ffa2ff6e482f5f1e1fda1912db19c747f43971b1fa54ae53c1ab1ce5d2f58566d6db4bc3016833 | ||||
|   checksum: 10/60497834b9acfb3b3994222509d359ecb9a197c885dfeb77e2050a287cd2f4ab19f00d5597172b47f9e0c54d9e1e13d8b2dd73322b7838599e1f16d1d6283f5b | ||||
|   languageName: node | ||||
|   linkType: hard | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user