diff --git a/landing-page/src/ha-landing-page.ts b/landing-page/src/ha-landing-page.ts index b1d64b02e5..d7a6434e16 100644 --- a/landing-page/src/ha-landing-page.ts +++ b/landing-page/src/ha-landing-page.ts @@ -100,7 +100,6 @@ class HaLandingPage extends LandingPageBaseElement { button-style native-name @value-changed=${this._languageChanged} - inline-arrow > pipeline.id === this._preferredPipeline + )?.name, + }), + }, + ]; + + if (this.includeLastUsed) { + options.unshift({ + value: LAST_USED, + label: this.hass.localize("ui.components.pipeline-picker.last_used"), + }); + } + + options.push( + ...this._pipelines.map((pipeline) => ({ + value: pipeline.id, + label: `${pipeline.name} (${formatLanguageCode(pipeline.language, this.hass.locale)})`, + })) + ); + return html` - ${this.includeLastUsed - ? html` - - ${this.hass!.localize( - "ui.components.pipeline-picker.last_used" - )} - - ` - : null} - - ${this.hass!.localize("ui.components.pipeline-picker.preferred", { - preferred: this._pipelines.find( - (pipeline) => pipeline.id === this._preferredPipeline - )?.name, - })} - - ${this._pipelines.map( - (pipeline) => - html` - ${pipeline.name} - (${formatLanguageCode(pipeline.language, this.hass.locale)}) - ` - )} `; } @@ -96,17 +94,17 @@ export class HaAssistPipelinePicker extends LitElement { } `; - private _changed(ev): void { - const target = ev.target as HaSelect; + private _changed(ev: CustomEvent<{ value: string }>): void { + const value = ev.detail.value; if ( !this.hass || - target.value === "" || - target.value === this.value || - (this.value === undefined && target.value === this._default) + value === "" || + value === this.value || + (this.value === undefined && value === this._default) ) { return; } - this.value = target.value === this._default ? undefined : target.value; + this.value = value === this._default ? undefined : value; fireEvent(this, "value-changed", { value: this.value }); } } diff --git a/src/components/ha-base-time-input.ts b/src/components/ha-base-time-input.ts index 40546aa13a..f24baf637a 100644 --- a/src/components/ha-base-time-input.ts +++ b/src/components/ha-base-time-input.ts @@ -4,10 +4,8 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; import { fireEvent } from "../common/dom/fire_event"; -import { stopPropagation } from "../common/dom/stop_propagation"; import "./ha-icon-button"; import "./ha-input-helper-text"; -import "./ha-list-item"; import "./ha-select"; import "./ha-textfield"; import type { HaTextField } from "./ha-textfield"; @@ -260,14 +258,10 @@ export class HaBaseTimeInput extends LitElement { .required=${this.required} .value=${this.amPm} .disabled=${this.disabled} - name="amPm" - naturalMenuWidth - fixedMenuPosition + .name=${"amPm"} @selected=${this._valueChanged} - @closed=${stopPropagation} + .options=${["AM", "PM"]} > - AM - PM `} ${this.helper @@ -282,10 +276,12 @@ export class HaBaseTimeInput extends LitElement { fireEvent(this, "value-changed"); } - private _valueChanged(ev: InputEvent) { + private _valueChanged(ev: InputEvent | CustomEvent<{ value: string }>): void { const textField = ev.currentTarget as HaTextField; this[textField.name] = - textField.name === "amPm" ? textField.value : Number(textField.value); + textField.name === "amPm" + ? (ev as CustomEvent<{ value: string }>).detail.value + : Number(textField.value); const value: TimeChangedEvent = { hours: this.hours, minutes: this.minutes, @@ -366,10 +362,6 @@ export class HaBaseTimeInput extends LitElement { ha-textfield:last-child { --text-field-border-top-right-radius: var(--mdc-shape-medium); } - ha-select { - --mdc-shape-small: 0; - width: 85px; - } :host([clearable]) .mdc-select__anchor { padding-inline-end: var(--select-selected-text-padding-end, 12px); } diff --git a/src/components/ha-blueprint-picker.ts b/src/components/ha-blueprint-picker.ts index 5c13ef894e..4cfe2ea1e6 100644 --- a/src/components/ha-blueprint-picker.ts +++ b/src/components/ha-blueprint-picker.ts @@ -2,12 +2,10 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../common/dom/fire_event"; -import { stopPropagation } from "../common/dom/stop_propagation"; import { stringCompare } from "../common/string/compare"; import type { Blueprint, BlueprintDomain, Blueprints } from "../data/blueprint"; import { fetchBlueprints } from "../data/blueprint"; import type { HomeAssistant } from "../types"; -import "./ha-list-item"; import "./ha-select"; @customElement("ha-blueprint-picker") @@ -55,20 +53,16 @@ class HaBluePrintPicker extends LitElement { - ${this._processedBlueprints(this.blueprints).map( - (blueprint) => html` - - ${blueprint.name} - - ` + .options=${this._processedBlueprints(this.blueprints).map( + (blueprint) => ({ + value: blueprint.path, + label: blueprint.name, + }) )} + > `; } @@ -82,8 +76,8 @@ class HaBluePrintPicker extends LitElement { } } - private _blueprintChanged(ev) { - const newValue = ev.target.value; + private _blueprintChanged(ev: CustomEvent<{ value: string }>) { + const newValue = ev.detail.value; if (newValue !== this.value) { this.value = newValue; diff --git a/src/components/ha-conversation-agent-picker.ts b/src/components/ha-conversation-agent-picker.ts index d29e8dc907..6ce20e2deb 100644 --- a/src/components/ha-conversation-agent-picker.ts +++ b/src/components/ha-conversation-agent-picker.ts @@ -3,7 +3,6 @@ import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; -import { stopPropagation } from "../common/dom/stop_propagation"; import { debounce } from "../common/util/debounce"; import type { ConfigEntry, SubEntry } from "../data/config_entries"; import { getConfigEntry, getSubEntries } from "../data/config_entries"; @@ -14,9 +13,8 @@ import { fetchIntegrationManifest } from "../data/integration"; import { showOptionsFlowDialog } from "../dialogs/config-flow/show-dialog-options-flow"; import { showSubConfigFlowDialog } from "../dialogs/config-flow/show-dialog-sub-config-flow"; import type { HomeAssistant } from "../types"; -import "./ha-list-item"; import "./ha-select"; -import type { HaSelect } from "./ha-select"; +import type { HaSelectOption } from "./ha-select"; const NONE = "__NONE_OPTION__"; @@ -73,6 +71,23 @@ export class HaConversationAgentPicker extends LitElement { value = NONE; } + const options: HaSelectOption[] = this._agents.map((agent) => ({ + value: agent.id, + label: agent.name, + disabled: + agent.supported_languages !== "*" && + agent.supported_languages.length === 0, + })); + + if (!this.required) { + options.unshift({ + value: NONE, + label: this.hass.localize( + "ui.components.conversation-agent-picker.none" + ), + }); + } + return html` - ${!this.required - ? html` - ${this.hass!.localize( - "ui.components.conversation-agent-picker.none" - )} - ` - : nothing} - ${this._agents.map( - (agent) => - html` - ${agent.name} - ` - )}${(this._subConfigEntry && this._configEntry?.supported_subentry_types[ this._subConfigEntry.subentry_type @@ -238,17 +234,17 @@ export class HaConversationAgentPicker extends LitElement { } `; - private _changed(ev): void { - const target = ev.target as HaSelect; + private _changed(ev: CustomEvent<{ value: string }>): void { + const value = ev.detail.value; if ( !this.hass || - target.value === "" || - target.value === this.value || - (this.value === undefined && target.value === NONE) + value === "" || + value === this.value || + (this.value === undefined && value === NONE) ) { return; } - this.value = target.value === NONE ? undefined : target.value; + this.value = value === NONE ? undefined : value; fireEvent(this, "value-changed", { value: this.value }); fireEvent(this, "supported-languages-changed", { value: this._agents!.find((agent) => agent.id === this.value) diff --git a/src/components/ha-language-picker.ts b/src/components/ha-language-picker.ts index 38807e046a..8725121a15 100644 --- a/src/components/ha-language-picker.ts +++ b/src/components/ha-language-picker.ts @@ -107,9 +107,6 @@ export class HaLanguagePicker extends LitElement { @property({ attribute: "no-sort", type: Boolean }) public noSort = false; - @property({ attribute: "inline-arrow", type: Boolean }) - public inlineArrow = false; - @state() _defaultLanguages: string[] = []; @query("ha-generic-picker", true) public genericPicker!: HaGenericPicker; diff --git a/src/components/ha-menu.ts b/src/components/ha-menu.ts deleted file mode 100644 index 1865bdf23c..0000000000 --- a/src/components/ha-menu.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { MenuBase } from "@material/mwc-menu/mwc-menu-base"; -import { styles } from "@material/mwc-menu/mwc-menu.css"; -import { html } from "lit"; -import { customElement } from "lit/decorators"; -import { classMap } from "lit/directives/class-map"; -import "./ha-list"; - -@customElement("ha-menu") -export class HaMenu extends MenuBase { - protected get listElement() { - if (!this.listElement_) { - this.listElement_ = this.renderRoot.querySelector("ha-list"); - return this.listElement_; - } - - return this.listElement_; - } - - protected renderList() { - const itemRoles = this.innerRole === "menu" ? "menuitem" : "option"; - const classes = this.renderListClasses(); - - return html` - - `; - } - - static styles = styles; -} - -declare global { - interface HTMLElementTagNameMap { - "ha-menu": HaMenu; - } -} diff --git a/src/components/ha-mount-picker.ts b/src/components/ha-mount-picker.ts index 7dfc1495a6..b13e2f8647 100644 --- a/src/components/ha-mount-picker.ts +++ b/src/components/ha-mount-picker.ts @@ -5,7 +5,6 @@ import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../common/config/is_component_loaded"; import { fireEvent } from "../common/dom/fire_event"; -import { stopPropagation } from "../common/dom/stop_propagation"; import { caseInsensitiveStringCompare } from "../common/string/compare"; import type { SupervisorMounts } from "../data/supervisor/mounts"; import { @@ -17,7 +16,7 @@ import type { HomeAssistant } from "../types"; import "./ha-alert"; import "./ha-list-item"; import "./ha-select"; -import type { HaSelect } from "./ha-select"; +import type { HaSelectOption } from "./ha-select"; const _BACKUP_DATA_DISK_ = "/backup"; @@ -52,60 +51,54 @@ class HaMountPicker extends LitElement { if (!this._mounts) { return nothing; } - const dataDiskOption = html` - - ${this.hass.localize("ui.components.mount-picker.use_datadisk") || - "Use data disk for backup"} - - - `; + + const options: HaSelectOption[] = this._filterMounts( + this._mounts, + this.usage + ).map((mount) => ({ + value: mount.name, + label: mount.name, + secondary: `${mount.server}${mount.port ? `:${mount.port}` : ""}${ + mount.type === SupervisorMountType.NFS ? mount.path : `:${mount.share}` + }`, + iconPath: + mount.usage === SupervisorMountUsage.MEDIA + ? mdiPlayBox + : mount.usage === SupervisorMountUsage.SHARE + ? mdiFolder + : mdiBackupRestore, + })); + + if (this.usage === SupervisorMountUsage.BACKUP) { + const dataDiskOption = { + value: _BACKUP_DATA_DISK_, + iconPath: mdiHarddisk, + label: + this.hass.localize("ui.components.mount-picker.use_datadisk") || + "Use data disk for backup", + }; + if ( + !this._mounts.default_backup_mount || + this._mounts.default_backup_mount === _BACKUP_DATA_DISK_ + ) { + options.unshift(dataDiskOption); + } else { + options.push(dataDiskOption); + } + } + return html` - ${this.usage === SupervisorMountUsage.BACKUP && - (!this._mounts.default_backup_mount || - this._mounts.default_backup_mount === _BACKUP_DATA_DISK_) - ? dataDiskOption - : nothing} - ${this._filterMounts(this._mounts, this.usage).map( - (mount) => - html` - ${mount.name} - ${mount.server}${mount.port - ? `:${mount.port}` - : nothing}${mount.type === SupervisorMountType.NFS - ? mount.path - : `:${mount.share}`} - - ` - )} - ${this.usage === SupervisorMountUsage.BACKUP && - this._mounts.default_backup_mount - ? dataDiskOption - : nothing} `; } @@ -153,16 +146,10 @@ class HaMountPicker extends LitElement { } } - private get _value() { - return this.value || ""; - } + private _mountChanged(ev: CustomEvent<{ value: string }>) { + const newValue = ev.detail.value; - private _mountChanged(ev: Event) { - ev.stopPropagation(); - const target = ev.target as HaSelect; - const newValue = target.value; - - if (newValue !== this._value) { + if (newValue !== this.value) { this._setValue(newValue); } } diff --git a/src/components/ha-select.ts b/src/components/ha-select.ts index 97b6bb91fd..2a9f369c26 100644 --- a/src/components/ha-select.ts +++ b/src/components/ha-select.ts @@ -1,187 +1,209 @@ -import { SelectBase } from "@material/mwc-select/mwc-select-base"; -import { styles } from "@material/mwc-select/mwc-select.css"; -import { mdiClose } from "@mdi/js"; -import { css, html, nothing } from "lit"; -import { customElement, property } from "lit/decorators"; -import { classMap } from "lit/directives/class-map"; -import { debounce } from "../common/util/debounce"; -import { nextRender } from "../common/util/render-status"; -import "./ha-icon-button"; -import "./ha-menu"; +import { css, html, LitElement, nothing } from "lit"; +import { customElement, property, query, state } from "lit/decorators"; +import { ifDefined } from "lit/directives/if-defined"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../common/dom/fire_event"; +import "./ha-dropdown"; +import "./ha-dropdown-item"; +import "./ha-picker-field"; +import type { HaPickerField } from "./ha-picker-field"; +import "./ha-svg-icon"; + +export interface HaSelectOption { + value: string; + label?: string; + secondary?: string; + iconPath?: string; + disabled?: boolean; +} @customElement("ha-select") -export class HaSelect extends SelectBase { - // @ts-ignore - @property({ type: Boolean }) public icon = false; +export class HaSelect extends LitElement { + @property({ type: Boolean }) public clearable = false; - @property({ type: Boolean, reflect: true }) public clearable = false; + @property({ attribute: false }) public options?: HaSelectOption[] | string[]; - @property({ attribute: "inline-arrow", type: Boolean }) - public inlineArrow = false; + @property() public label?: string; - @property() public options; + @property() public helper?: string; + + @property() public value?: string; + + @property({ type: Boolean }) public required = false; + + @property({ type: Boolean }) public disabled = false; + + @state() private _opened = false; + + @query("ha-picker-field") private _triggerField!: HaPickerField; + + private _getValueLabel = memoizeOne( + ( + options: HaSelectOption[] | string[] | undefined, + value: string | undefined + ) => { + if (!options || !value) { + return value; + } + + for (const option of options) { + if ( + (typeof option === "string" && option === value) || + (typeof option !== "string" && option.value === value) + ) { + return typeof option === "string" + ? option + : option.label || option.value; + } + } + + return value; + } + ); protected override render() { + if (this.disabled) { + return this._renderField(); + } + return html` - ${super.render()} - ${this.clearable && !this.required && !this.disabled && this.value - ? html`` - : nothing} + + ${this._renderField()} + ${this.options + ? this.options.map( + (option) => html` + + ${option.iconPath + ? html`` + : nothing} +
+ ${typeof option === "string" + ? option + : option.label || option.value} + ${option.secondary + ? html`
${option.secondary}
` + : nothing} +
+
+ ` + ) + : html``} +
`; } - protected override renderMenu() { - const classes = this.getMenuClasses(); - return html` - ${this.renderMenuContent()} - `; + private _renderField() { + const valueLabel = this._getValueLabel(this.options, this.value); + + return html` + + + `; } - protected override renderLeadingIcon() { - if (!this.icon) { - return nothing; + private _handleSelect(ev: CustomEvent<{ item: { value: string } }>) { + ev.stopPropagation(); + const value = ev.detail.item.value; + if (value === this.value) { + return; } - - return html``; - } - - connectedCallback() { - super.connectedCallback(); - window.addEventListener("translations-updated", this._translationsUpdated); - } - - protected async firstUpdated() { - super.firstUpdated(); - - if (this.inlineArrow) { - this.shadowRoot - ?.querySelector(".mdc-select__selected-text-container") - ?.classList.add("inline-arrow"); - } - } - - protected updated(changedProperties) { - super.updated(changedProperties); - - if (changedProperties.has("inlineArrow")) { - const textContainerElement = this.shadowRoot?.querySelector( - ".mdc-select__selected-text-container" - ); - if (this.inlineArrow) { - textContainerElement?.classList.add("inline-arrow"); - } else { - textContainerElement?.classList.remove("inline-arrow"); - } - } - if (changedProperties.get("options")) { - this.layoutOptions(); - this.selectByValue(this.value); - } - } - - disconnectedCallback() { - super.disconnectedCallback(); - window.removeEventListener( - "translations-updated", - this._translationsUpdated - ); + fireEvent(this, "selected", { value }); } private _clearValue(): void { if (this.disabled || !this.value) { return; } - this.valueSetDirectly = true; - this.select(-1); - this.mdcFoundation.handleChange(); + + fireEvent(this, "selected", { value: undefined }); } - private _translationsUpdated = debounce(async () => { - await nextRender(); - this.layoutOptions(); - }, 500); + private _handleShow() { + this.style.setProperty( + "--select-menu-width", + `${this._triggerField.offsetWidth}px` + ); + this._opened = true; + } - static override styles = [ - styles, - css` - :host([clearable]) { - position: relative; - } - .mdc-select:not(.mdc-select--disabled) .mdc-select__icon { - color: var(--secondary-text-color); - } - .mdc-select__anchor { - width: var(--ha-select-min-width, 200px); - } - .mdc-select--filled .mdc-select__anchor { - height: var(--ha-select-height, 56px); - } - .mdc-select--filled .mdc-floating-label { - inset-inline-start: var(--ha-space-4); - inset-inline-end: initial; - direction: var(--direction); - } - .mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label { - inset-inline-start: 48px; - inset-inline-end: initial; - direction: var(--direction); - } - .mdc-select .mdc-select__anchor { - padding-inline-start: var(--ha-space-4); - padding-inline-end: 0px; - direction: var(--direction); - } - .mdc-select__anchor .mdc-floating-label--float-above { - transform-origin: var(--float-start); - } - .mdc-select__selected-text-container { - padding-inline-end: var(--select-selected-text-padding-end, 0px); - } - :host([clearable]) .mdc-select__selected-text-container { - padding-inline-end: var( - --select-selected-text-padding-end, - var(--ha-space-4) - ); - } - ha-icon-button { - position: absolute; - top: 10px; - right: 28px; - --mdc-icon-button-size: 36px; - --mdc-icon-size: 20px; - color: var(--secondary-text-color); - inset-inline-start: initial; - inset-inline-end: 28px; - direction: var(--direction); - } - .inline-arrow { - flex-grow: 0; - } - `, - ]; + private _handleHide() { + this._opened = false; + } + + static styles = css` + :host { + position: relative; + } + ha-picker-field.opened { + --mdc-text-field-idle-line-color: var(--primary-color); + } + ha-dropdown-item.selected:hover { + background-color: var(--ha-color-fill-primary-quiet-hover); + } + + ha-dropdown-item .content { + display: flex; + gap: var(--ha-space-1); + flex-direction: column; + } + + ha-dropdown-item .secondary { + font-size: var(--ha-font-size-s); + color: var(--ha-color-text-secondary); + } + + ha-dropdown::part(menu) { + min-width: var(--select-menu-width); + } + + :host ::slotted(ha-dropdown-item.selected), + ha-dropdown-item.selected { + font-weight: var(--ha-font-weight-medium); + color: var(--primary-color); + background-color: var(--ha-color-fill-primary-quiet-resting); + --icon-primary-color: var(--primary-color); + } + `; } declare global { interface HTMLElementTagNameMap { "ha-select": HaSelect; } + + interface HASSDomEvents { + selected: { value: string | undefined }; + } } diff --git a/src/components/ha-selector/ha-selector-select.ts b/src/components/ha-selector/ha-selector-select.ts index 4a93bf095e..8ca3a10f6c 100644 --- a/src/components/ha-selector/ha-selector-select.ts +++ b/src/components/ha-selector/ha-selector-select.ts @@ -5,17 +5,16 @@ import { repeat } from "lit/directives/repeat"; import memoizeOne from "memoize-one"; import { ensureArray } from "../../common/array/ensure-array"; import { fireEvent } from "../../common/dom/fire_event"; -import { stopPropagation } from "../../common/dom/stop_propagation"; import { caseInsensitiveStringCompare } from "../../common/string/compare"; import type { SelectOption, SelectSelector } from "../../data/selector"; import type { HomeAssistant } from "../../types"; import "../chips/ha-chip-set"; import "../chips/ha-input-chip"; import "../ha-checkbox"; +import "../ha-dropdown-item"; import "../ha-formfield"; import "../ha-generic-picker"; import "../ha-input-helper-text"; -import "../ha-list-item"; import "../ha-radio"; import "../ha-select"; import "../ha-select-box"; @@ -231,24 +230,15 @@ export class HaSelectSelector extends LitElement { return html` - ${options.map( - (item: SelectOption) => html` - ${item.label} - ` - )} `; } @@ -295,7 +285,7 @@ export class HaSelectSelector extends LitElement { private _valueChanged(ev) { ev.stopPropagation(); - if (ev.detail?.index === -1 && this.value !== undefined) { + if (ev.detail?.value === undefined && this.value !== undefined) { fireEvent(this, "value-changed", { value: undefined, }); @@ -385,7 +375,7 @@ export class HaSelectSelector extends LitElement { ha-formfield { display: block; } - ha-list-item[disabled] { + ha-dropdown-item[disabled] { --mdc-theme-text-primary-on-background: var(--disabled-text-color); } ha-chip-set { diff --git a/src/components/ha-settings-row.ts b/src/components/ha-settings-row.ts index fa946d81f8..ed366a39f9 100644 --- a/src/components/ha-settings-row.ts +++ b/src/components/ha-settings-row.ts @@ -108,6 +108,7 @@ export class HaSettingsRow extends LitElement { white-space: normal; } .prefix-wrap { + flex: 1; display: var(--settings-row-prefix-display); } :host([narrow]) .prefix-wrap { diff --git a/src/components/ha-stt-picker.ts b/src/components/ha-stt-picker.ts index 246ba7c650..f0f10609b5 100644 --- a/src/components/ha-stt-picker.ts +++ b/src/components/ha-stt-picker.ts @@ -2,16 +2,14 @@ import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; -import { stopPropagation } from "../common/dom/stop_propagation"; +import { computeDomain } from "../common/entity/compute_domain"; import { computeStateName } from "../common/entity/compute_state_name"; import { debounce } from "../common/util/debounce"; import type { STTEngine } from "../data/stt"; import { listSTTEngines } from "../data/stt"; import type { HomeAssistant } from "../types"; -import "./ha-list-item"; import "./ha-select"; -import type { HaSelect } from "./ha-select"; -import { computeDomain } from "../common/entity/compute_domain"; +import type { HaSelectOption } from "./ha-select"; const NONE = "__NONE_OPTION__"; @@ -61,6 +59,30 @@ export class HaSTTPicker extends LitElement { value = NONE; } + const options: HaSelectOption[] = this._engines + .filter((engine) => !engine.deprecated || engine.engine_id !== value) + .map((engine) => { + let label: string; + if (engine.engine_id.includes(".")) { + const stateObj = this.hass.states[engine.engine_id]; + label = stateObj ? computeStateName(stateObj) : engine.engine_id; + } else { + label = engine.name || engine.engine_id; + } + return { + value: engine.engine_id, + label, + disabled: engine.supported_languages?.length === 0, + }; + }); + + if (this.required || value === NONE) { + options.unshift({ + value: NONE, + label: this.hass.localize("ui.components.stt-picker.none") || "None", + }); + } + return html` - ${!this.required - ? html` - ${this.hass!.localize("ui.components.stt-picker.none")} - ` - : nothing} - ${this._engines.map((engine) => { - if (engine.deprecated && engine.engine_id !== value) { - return nothing; - } - let label: string; - if (engine.engine_id.includes(".")) { - const stateObj = this.hass!.states[engine.engine_id]; - label = stateObj ? computeStateName(stateObj) : engine.engine_id; - } else { - label = engine.name || engine.engine_id; - } - return html` - ${label} - `; - })} `; } @@ -144,17 +141,17 @@ export class HaSTTPicker extends LitElement { } `; - private _changed(ev): void { - const target = ev.target as HaSelect; + private _changed(ev: CustomEvent<{ value: string }>): void { + const value = ev.detail.value; if ( !this.hass || - target.value === "" || - target.value === this.value || - (this.value === undefined && target.value === NONE) + value === "" || + value === this.value || + (this.value === undefined && value === NONE) ) { return; } - this.value = target.value === NONE ? undefined : target.value; + this.value = value === NONE ? undefined : value; fireEvent(this, "value-changed", { value: this.value }); fireEvent(this, "supported-languages-changed", { value: this._engines!.find((engine) => engine.engine_id === this.value) diff --git a/src/components/ha-theme-picker.ts b/src/components/ha-theme-picker.ts index c188e39309..02a220ef75 100644 --- a/src/components/ha-theme-picker.ts +++ b/src/components/ha-theme-picker.ts @@ -1,11 +1,10 @@ import type { TemplateResult } from "lit"; -import { css, html, nothing, LitElement } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; -import { stopPropagation } from "../common/dom/stop_propagation"; import type { HomeAssistant } from "../types"; import "./ha-select"; -import "./ha-list-item"; +import type { HaSelectOption } from "./ha-select"; const DEFAULT_THEME = "default"; @@ -25,6 +24,26 @@ export class HaThemePicker extends LitElement { @property({ type: Boolean }) public required = false; protected render(): TemplateResult { + const options: HaSelectOption[] = Object.keys( + this.hass?.themes.themes || {} + ).map((theme) => ({ + value: theme, + })); + + if (this.includeDefault) { + options.unshift({ + value: DEFAULT_THEME, + label: "Home Assistant", + }); + } + + if (!this.required) { + options.unshift({ + value: "remove", + label: this.hass!.localize("ui.components.theme-picker.no_theme"), + }); + } + return html` - ${!this.required - ? html` - - ${this.hass!.localize("ui.components.theme-picker.no_theme")} - - ` - : nothing} - ${this.includeDefault - ? html` - - Home Assistant - - ` - : nothing} - ${Object.keys(this.hass!.themes.themes) - .sort() - .map( - (theme) => - html`${theme}` - )} - + .options=${options} + > `; } @@ -67,11 +63,11 @@ export class HaThemePicker extends LitElement { } `; - private _changed(ev): void { - if (!this.hass || ev.target.value === "") { + private _changed(ev: CustomEvent<{ value: string }>): void { + if (!this.hass || ev.detail.value === "") { return; } - this.value = ev.target.value === "remove" ? undefined : ev.target.value; + this.value = ev.detail.value === "remove" ? undefined : ev.detail.value; fireEvent(this, "value-changed", { value: this.value }); } } diff --git a/src/components/ha-tts-picker.ts b/src/components/ha-tts-picker.ts index 73bdea15d5..51bcfa91f4 100644 --- a/src/components/ha-tts-picker.ts +++ b/src/components/ha-tts-picker.ts @@ -2,16 +2,14 @@ import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; -import { stopPropagation } from "../common/dom/stop_propagation"; +import { computeDomain } from "../common/entity/compute_domain"; import { computeStateName } from "../common/entity/compute_state_name"; import { debounce } from "../common/util/debounce"; import type { TTSEngine } from "../data/tts"; import { listTTSEngines } from "../data/tts"; import type { HomeAssistant } from "../types"; -import "./ha-list-item"; import "./ha-select"; -import type { HaSelect } from "./ha-select"; -import { computeDomain } from "../common/entity/compute_domain"; +import type { HaSelectOption } from "./ha-select"; const NONE = "__NONE_OPTION__"; @@ -61,6 +59,30 @@ export class HaTTSPicker extends LitElement { value = NONE; } + const options: HaSelectOption[] = this._engines + .filter((engine) => !engine.deprecated || engine.engine_id === value) + .map((engine) => { + let label: string; + if (engine.engine_id.includes(".")) { + const stateObj = this.hass.states[engine.engine_id]; + label = stateObj ? computeStateName(stateObj) : engine.engine_id; + } else { + label = engine.name || engine.engine_id; + } + return { + value: engine.engine_id, + label, + disabled: engine.supported_languages?.length === 0, + }; + }); + + if (!this.required || value === NONE) { + options.unshift({ + value: NONE, + label: this.hass.localize("ui.components.tts-picker.none"), + }); + } + return html` - ${!this.required - ? html` - ${this.hass!.localize("ui.components.tts-picker.none")} - ` - : nothing} - ${this._engines.map((engine) => { - if (engine.deprecated && engine.engine_id !== value) { - return nothing; - } - let label: string; - if (engine.engine_id.includes(".")) { - const stateObj = this.hass!.states[engine.engine_id]; - label = stateObj ? computeStateName(stateObj) : engine.engine_id; - } else { - label = engine.name || engine.engine_id; - } - return html` - ${label} - `; - })} `; } @@ -144,17 +141,17 @@ export class HaTTSPicker extends LitElement { } `; - private _changed(ev): void { - const target = ev.target as HaSelect; + private _changed(ev: CustomEvent<{ value: string }>): void { + const value = ev.detail.value; if ( !this.hass || - target.value === "" || - target.value === this.value || - (this.value === undefined && target.value === NONE) + value === "" || + value === this.value || + (this.value === undefined && value === NONE) ) { return; } - this.value = target.value === NONE ? undefined : target.value; + this.value = value === NONE ? undefined : value; fireEvent(this, "value-changed", { value: this.value }); fireEvent(this, "supported-languages-changed", { value: this._engines!.find((engine) => engine.engine_id === this.value) diff --git a/src/components/ha-tts-voice-picker.ts b/src/components/ha-tts-voice-picker.ts index c5ee4ac21a..0aad98de80 100644 --- a/src/components/ha-tts-voice-picker.ts +++ b/src/components/ha-tts-voice-picker.ts @@ -1,15 +1,13 @@ import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; -import { customElement, property, query, state } from "lit/decorators"; +import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; -import { stopPropagation } from "../common/dom/stop_propagation"; import { debounce } from "../common/util/debounce"; import type { TTSVoice } from "../data/tts"; import { listTTSVoices } from "../data/tts"; import type { HomeAssistant } from "../types"; -import "./ha-list-item"; import "./ha-select"; -import type { HaSelect } from "./ha-select"; +import type { HaSelectOption } from "./ha-select"; const NONE = "__NONE_OPTION__"; @@ -31,14 +29,25 @@ export class HaTTSVoicePicker extends LitElement { @state() _voices?: TTSVoice[] | null; - @query("ha-select") private _select?: HaSelect; - protected render() { if (!this._voices) { return nothing; } const value = this.value ?? (this.required ? this._voices[0]?.voice_id : NONE); + + const options: HaSelectOption[] = (this._voices || []).map((voice) => ({ + value: voice.voice_id, + label: voice.name, + })); + + if (!this.required || !this.value) { + options.unshift({ + value: NONE, + label: this.hass!.localize("ui.components.tts-voice-picker.none"), + }); + } + return html` - ${!this.required - ? html` - ${this.hass!.localize("ui.components.tts-voice-picker.none")} - ` - : nothing} - ${this._voices.map( - (voice) => - html` - ${voice.name} - ` - )} `; } @@ -102,34 +98,25 @@ export class HaTTSVoicePicker extends LitElement { } } - protected updated(changedProperties: PropertyValues) { - super.updated(changedProperties); - if ( - changedProperties.has("_voices") && - this._select?.value !== this.value - ) { - this._select?.layoutOptions(); - fireEvent(this, "value-changed", { value: this._select?.value }); - } - } - static styles = css` ha-select { width: 100%; + text-align: start; + display: block; } `; - private _changed(ev): void { - const target = ev.target as HaSelect; + private _changed(ev: CustomEvent<{ value: string }>): void { + const value = ev.detail.value; if ( !this.hass || - target.value === "" || - target.value === this.value || - (this.value === undefined && target.value === NONE) + value === "" || + value === this.value || + (this.value === undefined && value === NONE) ) { return; } - this.value = target.value === NONE ? undefined : target.value; + this.value = value === NONE ? undefined : value; fireEvent(this, "value-changed", { value: this.value }); } } diff --git a/src/dialogs/config-flow/previews/entity-preview-row.ts b/src/dialogs/config-flow/previews/entity-preview-row.ts index 7449c9325e..cfabdd8440 100644 --- a/src/dialogs/config-flow/previews/entity-preview-row.ts +++ b/src/dialogs/config-flow/previews/entity-preview-row.ts @@ -13,7 +13,6 @@ import "../../../components/ha-cover-controls"; import "../../../components/ha-cover-tilt-controls"; import "../../../components/ha-date-input"; import "../../../components/ha-humidifier-state"; -import "../../../components/ha-list-item"; import "../../../components/ha-select"; import "../../../components/ha-slider"; import "../../../components/ha-time-input"; @@ -296,17 +295,11 @@ class EntityPreviewRow extends LitElement { .label=${computeStateName(stateObj)} .value=${stateObj.state} .disabled=${isUnavailableState(stateObj.state)} - naturalMenuWidth + .options=${stateObj.attributes.options?.map((option) => ({ + value: option, + label: this.hass!.formatEntityState(stateObj, option), + })) || []} > - ${stateObj.attributes.options - ? stateObj.attributes.options.map( - (option) => html` - - ${this.hass!.formatEntityState(stateObj, option)} - - ` - ) - : ""} `; } diff --git a/src/dialogs/more-info/components/siren/ha-more-info-siren-advanced-controls.ts b/src/dialogs/more-info/components/siren/ha-more-info-siren-advanced-controls.ts index ce0735bde7..349e74ad9f 100644 --- a/src/dialogs/more-info/components/siren/ha-more-info-siren-advanced-controls.ts +++ b/src/dialogs/more-info/components/siren/ha-more-info-siren-advanced-controls.ts @@ -4,16 +4,15 @@ import type { CSSResultGroup } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { stopPropagation } from "../../../../common/dom/stop_propagation"; import { supportsFeature } from "../../../../common/entity/supports-feature"; import "../../../../components/ha-button"; import "../../../../components/ha-control-button"; import "../../../../components/ha-dialog-footer"; import "../../../../components/ha-icon-button"; import "../../../../components/ha-list-item"; -import "../../../../components/ha-wa-dialog"; import "../../../../components/ha-select"; import "../../../../components/ha-textfield"; +import "../../../../components/ha-wa-dialog"; import { SirenEntityFeature } from "../../../../data/siren"; import { haStyle } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; @@ -77,24 +76,19 @@ class MoreInfoSirenAdvancedControls extends LitElement { ? html` ({ + value: Array.isArray( + this._stateObj!.attributes.available_tones + ) + ? toneName + : toneId, + label: toneName, + }))} > - ${Object.entries( - this._stateObj.attributes.available_tones - ).map( - ([toneId, toneName]) => html` - ${toneName} - ` - )} ` : nothing} @@ -152,8 +146,8 @@ class MoreInfoSirenAdvancedControls extends LitElement { `; } - private _handleToneChange(ev) { - this._tone = ev.target.value; + private _handleToneChange(ev: CustomEvent<{ value: string }>) { + this._tone = ev.detail.value; } private _handleVolumeChange(ev) { diff --git a/src/dialogs/more-info/controls/more-info-remote.ts b/src/dialogs/more-info/controls/more-info-remote.ts index 662ae4eb7c..fd425420f9 100644 --- a/src/dialogs/more-info/controls/more-info-remote.ts +++ b/src/dialogs/more-info/controls/more-info-remote.ts @@ -1,12 +1,10 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property } from "lit/decorators"; -import { stopPropagation } from "../../../common/dom/stop_propagation"; import { supportsFeature } from "../../../common/entity/supports-feature"; +import "../../../components/ha-select"; import type { RemoteEntity } from "../../../data/remote"; import { REMOTE_SUPPORT_ACTIVITY } from "../../../data/remote"; import type { HomeAssistant } from "../../../types"; -import "../../../components/ha-select"; -import "../../../components/ha-list-item"; @customElement("more-info-remote") class MoreInfoRemote extends LitElement { @@ -30,30 +28,24 @@ class MoreInfoRemote extends LitElement { )} .value=${stateObj.attributes.current_activity || ""} @selected=${this._handleActivityChanged} - fixedMenuPosition - naturalMenuWidth - @closed=${stopPropagation} + .options=${stateObj.attributes.activity_list?.map((activity) => ({ + value: activity, + label: this.hass!.formatEntityAttributeValue( + stateObj, + "activity", + activity + ), + }))} > - ${stateObj.attributes.activity_list?.map( - (activity) => html` - - ${this.hass.formatEntityAttributeValue( - stateObj, - "activity", - activity - )} - - ` - )} ` : nothing} `; } - private _handleActivityChanged(ev) { + private _handleActivityChanged(ev: CustomEvent<{ value: string }>) { const oldVal = this.stateObj!.attributes.current_activity; - const newVal = ev.target.value; + const newVal = ev.detail.value; if (!newVal || oldVal === newVal) { return; diff --git a/src/dialogs/more-info/controls/more-info-vacuum.ts b/src/dialogs/more-info/controls/more-info-vacuum.ts index 4a68280b05..97593410d1 100644 --- a/src/dialogs/more-info/controls/more-info-vacuum.ts +++ b/src/dialogs/more-info/controls/more-info-vacuum.ts @@ -11,13 +11,11 @@ import { import { LitElement, css, html, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import memoizeOne from "memoize-one"; -import { stopPropagation } from "../../../common/dom/stop_propagation"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { supportsFeature } from "../../../common/entity/supports-feature"; import "../../../components/entity/ha-battery-icon"; import "../../../components/ha-icon"; import "../../../components/ha-icon-button"; -import "../../../components/ha-list-item"; import "../../../components/ha-select"; import { UNAVAILABLE } from "../../../data/entity/entity"; import type { EntityRegistryDisplayEntry } from "../../../data/entity/entity_registry"; @@ -172,21 +170,17 @@ class MoreInfoVacuum extends LitElement { .disabled=${stateObj.state === UNAVAILABLE} .value=${stateObj.attributes.fan_speed} @selected=${this._handleFanSpeedChanged} - fixedMenuPosition - naturalMenuWidth - @closed=${stopPropagation} - > - ${stateObj.attributes.fan_speed_list!.map( - (mode) => html` - - ${this.hass.formatEntityAttributeValue( - stateObj, - "fan_speed", - mode - )} - - ` + .options=${stateObj.attributes.fan_speed_list!.map( + (mode) => ({ + value: mode, + label: this.hass!.formatEntityAttributeValue( + stateObj, + "fan_speed", + mode + ), + }) )} + >
) { const oldVal = this.stateObj!.attributes.fan_speed; - const newVal = ev.target.value; + const newVal = ev.detail.value; if (!newVal || oldVal === newVal) { return; diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-check.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-check.ts index e3dc60e5c3..07760dd358 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-check.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-check.ts @@ -5,8 +5,8 @@ import "../../components/ha-button"; import "../../components/ha-spinner"; import { testAssistSatelliteConnection } from "../../data/assist_satellite"; import type { HomeAssistant } from "../../types"; -import { AssistantSetupStyles } from "./styles"; import { documentationUrl } from "../../util/documentation-url"; +import { AssistantSetupStyles } from "./styles"; @customElement("ha-voice-assistant-setup-step-check") export class HaVoiceAssistantSetupStepCheck extends LitElement { @@ -58,7 +58,7 @@ export class HaVoiceAssistantSetupStepCheck extends LitElement { "/voice_control/troubleshooting/#i-dont-get-a-voice-response" )} > - >${this.hass.localize( + ${this.hass.localize( "ui.panel.config.voice_assistants.satellite_wizard.check.help" )} diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-success.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-success.ts index c636ff916f..2bf15e4c07 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-success.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-success.ts @@ -8,7 +8,6 @@ import { computeDeviceName, computeDeviceNameDisplay, } from "../../common/entity/compute_device_name"; -import "../../components/ha-list-item"; import "../../components/ha-select"; import "../../components/ha-tts-voice-picker"; import type { AssistPipeline } from "../../data/assist_pipeline"; @@ -115,19 +114,15 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement { .label=${this.hass.localize( "ui.panel.config.voice_assistants.assistants.pipeline.detail.form.wake_word_id" )} - @closed=${stopPropagation} - fixedMenuPosition - naturalMenuWidth .value=${this.assistConfiguration.active_wake_words[0]} @selected=${this._wakeWordPicked} - > - ${this.assistConfiguration.available_wake_words.map( - (wakeword) => - html` - ${wakeword.wake_word} - ` + .options=${this.assistConfiguration.available_wake_words.map( + (wakeword) => ({ + value: wakeword.id, + label: wakeword.wake_word, + }) )} - + > - ${pipelineEntity?.attributes.options.map( - (pipeline) => - html` - ${this.hass.formatEntityState(pipelineEntity, pipeline)} - ` + .options=${pipelineEntity?.attributes.options.map( + (pipeline) => ({ + value: pipeline, + label: this.hass.formatEntityState( + pipelineEntity, + pipeline + ), + }) )} + > ) { + const option = ev.detail.value; + if (this.assistConfiguration) { + this.assistConfiguration.active_wake_words = [option]; + } await setWakeWords(this.hass, this.assistEntityId!, [option]); } - private _pipelinePicked(ev) { + private _pipelinePicked(ev: CustomEvent<{ value: string }>) { const stateObj = this.hass!.states[ this.assistConfiguration!.pipeline_entity_id ] as InputSelectEntity; - const option = ev.target.value; + const option = ev.detail.value; if ( option === stateObj.state || !stateObj.attributes.options.includes(option) @@ -384,6 +383,11 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement { .row ha-button { width: 82px; } + + ha-select { + display: block; + text-align: start; + } `, ]; } diff --git a/src/onboarding/ha-onboarding.ts b/src/onboarding/ha-onboarding.ts index ce19271d23..65f0167a6f 100644 --- a/src/onboarding/ha-onboarding.ts +++ b/src/onboarding/ha-onboarding.ts @@ -144,7 +144,6 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) { .label=${""} native-name @value-changed=${this._languageChanged} - inline-arrow > { - selectElement.select(changedProps.has("dtstart") ? 0 : oldSelected); - }); - } } if ( @@ -184,35 +170,16 @@ export class RecurrenceRuleEditor extends LitElement { id="freq" label=${this.hass.localize("ui.components.calendar.event.repeat.label")} @selected=${this._onRepeatSelected} - @closed=${stopPropagation} - fixedMenuPosition - naturalMenuWidth .value=${this._freq} - > - - ${this.hass.localize("ui.components.calendar.event.repeat.freq.none")} - - - ${this.hass.localize( - "ui.components.calendar.event.repeat.freq.yearly" - )} - - - ${this.hass.localize( - "ui.components.calendar.event.repeat.freq.monthly" - )} - - - ${this.hass.localize( - "ui.components.calendar.event.repeat.freq.weekly" - )} - - - ${this.hass.localize( - "ui.components.calendar.event.repeat.freq.daily" - )} - - + .options=${["none", "yearly", "monthly", "weekly", "daily"].map( + (freq) => ({ + value: freq, + label: this.hass.localize( + `ui.components.calendar.event.repeat.freq.${freq}` as LocalizeKeys + ), + }) + )} + > `; } @@ -227,18 +194,8 @@ export class RecurrenceRuleEditor extends LitElement { )} @selected=${this._onMonthlyDetailSelected} .value=${this._monthlyRepeat || this._monthlyRepeatItems[0]?.value} - @closed=${stopPropagation} - fixedMenuPosition - naturalMenuWidth - > - ${this._monthlyRepeatItems!.map( - (item) => html` - - ${item.label} - - ` - )} - ` + .options=${this._monthlyRepeatItems} + >` : nothing} `; } @@ -299,19 +256,13 @@ export class RecurrenceRuleEditor extends LitElement { )} .value=${this._end} @selected=${this._onEndSelected} - @closed=${stopPropagation} - fixedMenuPosition - naturalMenuWidth + .options=${["never", "after", "on"].map((end) => ({ + value: end, + label: this.hass.localize( + `ui.components.calendar.event.repeat.end.${end as RepeatEnd}` + ), + }))} > - - ${this.hass.localize("ui.components.calendar.event.repeat.end.never")} - - - ${this.hass.localize("ui.components.calendar.event.repeat.end.after")} - - - ${this.hass.localize("ui.components.calendar.event.repeat.end.on")} - ${this._end === "after" ? html` @@ -360,8 +311,8 @@ export class RecurrenceRuleEditor extends LitElement { this._interval = (e.target! as any).value; } - private _onRepeatSelected(e: CustomEvent>) { - this._freq = (e.target as HaSelect).value as RepeatFrequency; + private _onRepeatSelected(e: CustomEvent<{ value: string }>) { + this._freq = e.detail.value as RepeatFrequency; if (this._freq === "yearly") { this._interval = 1; @@ -370,12 +321,12 @@ export class RecurrenceRuleEditor extends LitElement { this._weekday.clear(); this._computeWeekday(); } - e.stopPropagation(); } - private _onMonthlyDetailSelected(e: CustomEvent>) { - e.stopPropagation(); - const selectedItem = this._monthlyRepeatItems[e.detail.index]; + private _onMonthlyDetailSelected(e: CustomEvent<{ value: string }>) { + const selectedItem = this._monthlyRepeatItems.find( + (item) => item.value === e.detail.value + ); if (!selectedItem) { return; } @@ -395,8 +346,8 @@ export class RecurrenceRuleEditor extends LitElement { this.requestUpdate("_weekday"); } - private _onEndSelected(e: CustomEvent>) { - const end = (e.target as HaSelect).value as RepeatEnd; + private _onEndSelected(e: CustomEvent<{ value: string }>) { + const end = e.detail.value as RepeatEnd; if (end === this._end) { return; } diff --git a/src/panels/config/apps/app-view/config/supervisor-app-audio.ts b/src/panels/config/apps/app-view/config/supervisor-app-audio.ts index 8c39d11d4e..a29ce05a64 100644 --- a/src/panels/config/apps/app-view/config/supervisor-app-audio.ts +++ b/src/panels/config/apps/app-view/config/supervisor-app-audio.ts @@ -1,11 +1,9 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { stopPropagation } from "../../../../../common/dom/stop_propagation"; import "../../../../../components/buttons/ha-progress-button"; import "../../../../../components/ha-alert"; import "../../../../../components/ha-card"; -import "../../../../../components/ha-list-item"; import "../../../../../components/ha-select"; import type { HassioAddonDetails, @@ -16,8 +14,8 @@ import type { HassioHardwareAudioDevice } from "../../../../../data/hassio/hardw import { fetchHassioHardwareAudio } from "../../../../../data/hassio/hardware"; import { haStyle } from "../../../../../resources/styles"; import type { HomeAssistant } from "../../../../../types"; -import { suggestSupervisorAppRestart } from "../dialogs/suggestSupervisorAppRestart"; import { supervisorAppsStyle } from "../../resources/supervisor-apps-style"; +import { suggestSupervisorAppRestart } from "../dialogs/suggestSupervisorAppRestart"; @customElement("supervisor-app-audio") class SupervisorAppAudio extends LitElement { @@ -55,19 +53,13 @@ class SupervisorAppAudio extends LitElement { "ui.panel.config.apps.configuration.audio.input" )} @selected=${this._setInputDevice} - @closed=${stopPropagation} - fixedMenuPosition - naturalMenuWidth .value=${this._selectedInput!} .disabled=${this.disabled} + .options=${this._inputDevices.map((item) => ({ + value: item.device || "", + label: item.name, + }))} > - ${this._inputDevices.map( - (item) => html` - - ${item.name} - - ` - )} `} ${this._outputDevices && html` ({ + value: item.device || "", + label: item.name, + }))} > - ${this._outputDevices.map( - (item) => html` - ${item.name} - ` - )} `}
@@ -116,6 +102,7 @@ class SupervisorAppAudio extends LitElement { } ha-select { width: 100%; + display: block; } ha-select:last-child { margin-top: var(--ha-space-2); @@ -131,13 +118,13 @@ class SupervisorAppAudio extends LitElement { } } - private _setInputDevice(ev): void { - const device = ev.target.value; + private _setInputDevice(ev: CustomEvent<{ value: string }>): void { + const device = ev.detail.value; this._selectedInput = device; } - private _setOutputDevice(ev): void { - const device = ev.target.value; + private _setOutputDevice(ev: CustomEvent<{ value: string }>): void { + const device = ev.detail.value; this._selectedOutput = device; } diff --git a/src/panels/config/automation/action/types/ha-automation-action-condition.ts b/src/panels/config/automation/action/types/ha-automation-action-condition.ts index 9c99aceb1d..a1caca0371 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-condition.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-condition.ts @@ -2,13 +2,11 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../../common/dom/fire_event"; -import { stopPropagation } from "../../../../../common/dom/stop_propagation"; import { stringCompare } from "../../../../../common/string/compare"; import type { LocalizeFunc } from "../../../../../common/translations/localize"; import { CONDITION_ICONS } from "../../../../../components/ha-condition-icon"; -import "../../../../../components/ha-list-item"; +import "../../../../../components/ha-dropdown-item"; import "../../../../../components/ha-select"; -import type { HaSelect } from "../../../../../components/ha-select"; import { DYNAMIC_PREFIX, getValueFromDynamic, @@ -85,37 +83,47 @@ export class HaConditionAction this.action.condition ); + const value = + this.action.condition in this._conditionDescriptions + ? `${DYNAMIC_PREFIX}${this.action.condition}` + : this.action.condition; + + let valueLabel = value; + + const items = html`${this._processedTypes( + this._conditionDescriptions, + this.hass.localize + ).map(([opt, label, condition]) => { + const selected = value === opt; + + if (selected) { + valueLabel = label; + } + + return html` + + + ${label} + + `; + })}`; + return html` ${this.inSidebar || (!this.inSidebar && !this.indent) ? html` - ${this._processedTypes( - this._conditionDescriptions, - this.hass.localize - ).map( - ([opt, label, condition]) => html` - - ${label} - - - ` - )} + ${items} ` : nothing} @@ -192,8 +200,8 @@ export class HaConditionAction }); } - private _typeChanged(ev: CustomEvent) { - const type = (ev.target as HaSelect).value; + private _typeChanged(ev: CustomEvent<{ value: string }>) { + const type = ev.detail.value; if (!type) { return; @@ -242,6 +250,7 @@ export class HaConditionAction static styles = css` ha-select { margin-bottom: 24px; + display: block; } `; } diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-tag.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-tag.ts index 224b9903d1..0119fe1002 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-tag.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-tag.ts @@ -4,7 +4,6 @@ import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../../../common/dom/fire_event"; import { caseInsensitiveStringCompare } from "../../../../../common/string/compare"; import "../../../../../components/ha-select"; -import "../../../../../components/ha-list-item"; import type { TagTrigger } from "../../../../../data/automation"; import type { Tag } from "../../../../../data/tag"; import { fetchTags } from "../../../../../data/tag"; @@ -42,16 +41,11 @@ export class HaTagTrigger extends LitElement implements TriggerElement { .disabled=${this.disabled || this._tags.length === 0} .value=${this.trigger.tag_id} @selected=${this._tagChanged} - fixedMenuPosition - naturalMenuWidth + .options=${this._tags.map((tag) => ({ + value: tag.id, + label: tag.name || tag.id, + }))} > - ${this._tags.map( - (tag) => html` - - ${tag.name || tag.id} - - ` - )} `; } @@ -66,18 +60,18 @@ export class HaTagTrigger extends LitElement implements TriggerElement { ); } - private _tagChanged(ev) { + private _tagChanged(ev: CustomEvent<{ value: string }>) { if ( - !ev.target.value || + !ev.detail.value || !this._tags || - this.trigger.tag_id === ev.target.value + this.trigger.tag_id === ev.detail.value ) { return; } fireEvent(this, "value-changed", { value: { ...this.trigger, - tag_id: ev.target.value, + tag_id: ev.detail.value, }, }); } diff --git a/src/panels/config/cloud/account/cloud-tts-pref.ts b/src/panels/config/cloud/account/cloud-tts-pref.ts index 959fbb0fc6..b0602cfd93 100644 --- a/src/panels/config/cloud/account/cloud-tts-pref.ts +++ b/src/panels/config/cloud/account/cloud-tts-pref.ts @@ -1,12 +1,12 @@ -import { css, html, LitElement, nothing } from "lit"; import { mdiContentCopy } from "@mdi/js"; +import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../common/dom/fire_event"; -import "../../../../components/ha-card"; +import { copyToClipboard } from "../../../../common/util/copy-clipboard"; import "../../../../components/ha-button"; +import "../../../../components/ha-card"; import "../../../../components/ha-language-picker"; -import "../../../../components/ha-list-item"; import "../../../../components/ha-select"; import "../../../../components/ha-svg-icon"; import "../../../../components/ha-switch"; @@ -19,9 +19,8 @@ import { } from "../../../../data/cloud/tts"; import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box"; import type { HomeAssistant } from "../../../../types"; -import { showTryTtsDialog } from "./show-dialog-cloud-tts-try"; -import { copyToClipboard } from "../../../../common/util/copy-clipboard"; import { showToast } from "../../../../util/toast"; +import { showTryTtsDialog } from "./show-dialog-cloud-tts-try"; export const getCloudTtsSupportedVoices = ( language: string, @@ -96,13 +95,11 @@ export class CloudTTSPref extends LitElement { .disabled=${this.savingPreferences} .value=${defaultVoice[1]} @selected=${this._handleVoiceChange} + .options=${voices.map((voice) => ({ + value: voice.voiceId, + label: voice.voiceName, + }))} > - ${voices.map( - (voice) => - html` - ${voice.voiceName} - ` - )}
@@ -134,16 +131,6 @@ export class CloudTTSPref extends LitElement { `; } - protected updated(changedProps) { - if ( - changedProps.has("cloudStatus") && - this.cloudStatus?.prefs.tts_default_voice?.[0] !== - changedProps.get("cloudStatus")?.prefs.tts_default_voice?.[0] - ) { - this.renderRoot.querySelector("ha-select")?.layoutOptions(); - } - } - protected willUpdate(changedProps) { super.willUpdate(changedProps); if (!this.hasUpdated) { @@ -195,13 +182,13 @@ export class CloudTTSPref extends LitElement { } } - private async _handleVoiceChange(ev) { - if (ev.target.value === this.cloudStatus!.prefs.tts_default_voice[1]) { + private async _handleVoiceChange(ev: CustomEvent<{ value: string }>) { + if (ev.detail.value === this.cloudStatus!.prefs.tts_default_voice[1]) { return; } this.savingPreferences = true; const language = this.cloudStatus!.prefs.tts_default_voice[0]; - const voice = ev.target.value; + const voice = ev.detail.value; try { await updateCloudPref(this.hass, { diff --git a/src/panels/config/cloud/account/dialog-cloud-tts-try.ts b/src/panels/config/cloud/account/dialog-cloud-tts-try.ts index 1d0ecbdb0e..35f22fa16e 100644 --- a/src/panels/config/cloud/account/dialog-cloud-tts-try.ts +++ b/src/panels/config/cloud/account/dialog-cloud-tts-try.ts @@ -4,14 +4,13 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { storage } from "../../../../common/decorators/storage"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { stopPropagation } from "../../../../common/dom/stop_propagation"; import { computeStateDomain } from "../../../../common/entity/compute_state_domain"; import { computeStateName } from "../../../../common/entity/compute_state_name"; import { supportsFeature } from "../../../../common/entity/supports-feature"; -import { createCloseHeading } from "../../../../components/ha-dialog"; -import "../../../../components/ha-list-item"; -import "../../../../components/ha-select"; import "../../../../components/ha-button"; +import { createCloseHeading } from "../../../../components/ha-dialog"; +import "../../../../components/ha-select"; +import type { HaSelectOption } from "../../../../components/ha-select"; import "../../../../components/ha-textarea"; import type { HaTextArea } from "../../../../components/ha-textarea"; import { showAutomationEditor } from "../../../../data/automation"; @@ -60,6 +59,25 @@ export class DialogTryTts extends LitElement { return nothing; } const target = this._target || "browser"; + + const targetOptions: HaSelectOption[] = Object.values(this.hass.states) + .filter( + (entity) => + computeStateDomain(entity) === "media_player" && + supportsFeature(entity, MediaPlayerEntityFeature.PLAY_MEDIA) + ) + .map((entity) => ({ + value: entity.entity_id, + label: computeStateName(entity), + })); + + targetOptions.unshift({ + value: "browser", + label: this.hass.localize( + "ui.panel.config.cloud.account.tts.dialog.target_browser" + ), + }); + return html` - - ${this.hass.localize( - "ui.panel.config.cloud.account.tts.dialog.target_browser" - )} - - ${Object.values(this.hass.states) - .filter( - (entity) => - computeStateDomain(entity) === "media_player" && - supportsFeature(entity, MediaPlayerEntityFeature.PLAY_MEDIA) - ) - .map( - (entity) => html` - - ${computeStateName(entity)} - - ` - )} ) { + this._target = ev.detail.value; this.requestUpdate("_target"); } @@ -227,10 +225,9 @@ export class DialogTryTts extends LitElement { } ha-textarea, ha-select { - width: 100%; - } - ha-select { + display: block; margin-top: 8px; + width: 100%; } `, ]; diff --git a/src/panels/config/energy/dialogs/dialog-energy-device-settings-water.ts b/src/panels/config/energy/dialogs/dialog-energy-device-settings-water.ts index 7e2d625469..1d3c90145b 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-device-settings-water.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-device-settings-water.ts @@ -3,14 +3,12 @@ import type { CSSResultGroup } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { stopPropagation } from "../../../../common/dom/stop_propagation"; import "../../../../components/entity/ha-entity-picker"; import "../../../../components/entity/ha-statistic-picker"; +import "../../../../components/ha-button"; import "../../../../components/ha-dialog"; import "../../../../components/ha-radio"; -import "../../../../components/ha-button"; import "../../../../components/ha-select"; -import "../../../../components/ha-list-item"; import type { DeviceConsumptionEnergyPreference } from "../../../../data/energy"; import { energyStatisticHelpUrl } from "../../../../data/energy"; import { getStatisticLabel } from "../../../../data/recorder"; @@ -95,6 +93,27 @@ export class DialogEnergyDeviceSettingsWater const pickableUnit = this._volume_units?.join(", ") || ""; + const includedInDeviceOptions = !this._possibleParents.length + ? [ + { + value: "-", + disabled: true, + label: this.hass.localize( + "ui.panel.config.energy.device_consumption_water.dialog.no_upstream_devices" + ), + }, + ] + : this._possibleParents.map((stat) => ({ + value: stat.stat_consumption, + label: + stat.name || + getStatisticLabel( + this.hass, + stat.stat_consumption, + this._params?.statsMetadata?.[stat.stat_consumption] + ), + })); + return html` - ${!this._possibleParents.length - ? html` - ${this.hass.localize( - "ui.panel.config.energy.device_consumption_water.dialog.no_upstream_devices" - )} - ` - : this._possibleParents.map( - (stat) => html` - ${stat.name || - getStatisticLabel( - this.hass, - stat.stat_consumption, - this._params?.statsMetadata?.[stat.stat_consumption] - )} - ` - )} ) { const newDevice = { ...this._device!, - included_in_stat: ev.target!.value, + included_in_stat: ev.detail.value, } as DeviceConsumptionEnergyPreference; if (!newDevice.included_in_stat) { delete newDevice.included_in_stat; @@ -249,6 +246,7 @@ export class DialogEnergyDeviceSettingsWater width: 100%; } ha-select { + display: block; margin-top: 16px; width: 100%; } diff --git a/src/panels/config/energy/dialogs/dialog-energy-device-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-device-settings.ts index afc2d732ae..9865f7c361 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-device-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-device-settings.ts @@ -3,14 +3,13 @@ import type { CSSResultGroup } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { stopPropagation } from "../../../../common/dom/stop_propagation"; import "../../../../components/entity/ha-entity-picker"; import "../../../../components/entity/ha-statistic-picker"; +import "../../../../components/ha-button"; import "../../../../components/ha-dialog"; import "../../../../components/ha-radio"; -import "../../../../components/ha-button"; import "../../../../components/ha-select"; -import "../../../../components/ha-list-item"; +import type { HaSelectOption } from "../../../../components/ha-select"; import type { DeviceConsumptionEnergyPreference } from "../../../../data/energy"; import { energyStatisticHelpUrl } from "../../../../data/energy"; import { getStatisticLabel } from "../../../../data/recorder"; @@ -104,6 +103,28 @@ export class DialogEnergyDeviceSettings return nothing; } + const includedInDeviceOptions: HaSelectOption[] = this._possibleParents + .length + ? this._possibleParents.map((stat) => ({ + value: stat.stat_consumption, + label: + stat.name || + getStatisticLabel( + this.hass, + stat.stat_consumption, + this._params?.statsMetadata?.[stat.stat_consumption] + ), + })) + : [ + { + value: "-", + disabled: true, + label: this.hass.localize( + "ui.panel.config.energy.device_consumption.dialog.no_upstream_devices" + ), + }, + ]; + return html` - ${!this._possibleParents.length - ? html` - ${this.hass.localize( - "ui.panel.config.energy.device_consumption.dialog.no_upstream_devices" - )} - ` - : this._possibleParents.map( - (stat) => html` - ${stat.name || - getStatisticLabel( - this.hass, - stat.stat_consumption, - this._params?.statsMetadata?.[stat.stat_consumption] - )} - ` - )} ) { const newDevice = { ...this._device!, - included_in_stat: ev.target!.value, + included_in_stat: ev.detail.value, } as DeviceConsumptionEnergyPreference; if (!newDevice.included_in_stat) { delete newDevice.included_in_stat; @@ -289,6 +288,7 @@ export class DialogEnergyDeviceSettings width: 100%; } ha-select { + display: block; margin-top: var(--ha-space-4); width: 100%; } diff --git a/src/panels/config/entities/entity-registry-settings-editor.ts b/src/panels/config/entities/entity-registry-settings-editor.ts index 18c9f179c1..599eabce1a 100644 --- a/src/panels/config/entities/entity-registry-settings-editor.ts +++ b/src/panels/config/entities/entity-registry-settings-editor.ts @@ -1,3 +1,4 @@ +import "@home-assistant/webawesome/dist/components/divider/divider"; import { mdiContentCopy, mdiRestore } from "@mdi/js"; import type { HassEntity } from "home-assistant-js-websocket"; import type { CSSResultGroup, PropertyValues } from "lit"; @@ -7,7 +8,6 @@ import { until } from "lit/directives/until"; import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { fireEvent } from "../../../common/dom/fire_event"; -import { stopPropagation } from "../../../common/dom/stop_propagation"; import { computeDomain } from "../../../common/entity/compute_domain"; import { computeObjectId } from "../../../common/entity/compute_object_id"; import { supportsFeature } from "../../../common/entity/supports-feature"; @@ -22,6 +22,7 @@ import { copyToClipboard } from "../../../common/util/copy-clipboard"; import "../../../components/ha-alert"; import "../../../components/ha-area-picker"; import "../../../components/ha-color-picker"; +import "../../../components/ha-dropdown-item"; import "../../../components/ha-icon"; import "../../../components/ha-icon-button-next"; import "../../../components/ha-icon-picker"; @@ -424,34 +425,46 @@ export class EntityRegistrySettingsEditor extends LitElement { .label=${this.hass.localize( "ui.dialogs.entity_registry.editor.device_class" )} - naturalMenuWidth - fixedMenuPosition @selected=${this._switchAsDomainChanged} - @closed=${stopPropagation} + value=${this._switchAsLabel( + this._switchAsDomain, + this._deviceClass + )} > - ${domainToName(this.hass.localize, "switch")} - - + ${this.hass.localize( "ui.dialogs.entity_registry.editor.device_classes.switch.outlet" )} - -
  • + + ${this._switchAsDomainsSorted( SWITCH_AS_DOMAINS, this.hass.localize ).map( (entry) => html` - + ${entry.label} - + ` )} ` @@ -460,19 +473,22 @@ export class EntityRegistrySettingsEditor extends LitElement { .label=${this.hass.localize( "ui.dialogs.entity_registry.editor.switch_as_x" )} - .value=${this._switchAsDomain} - naturalMenuWidth - fixedMenuPosition + .value=${this._switchAsLabel(this._switchAsDomain)} @selected=${this._switchAsDomainChanged} - @closed=${stopPropagation} > - + ${domainToName(this.hass.localize, "switch")} - - + + ${domainToName(this.hass.localize, domain)} - -
  • + + ${this._switchAsDomainsSorted( SWITCH_AS_DOMAINS, this.hass.localize @@ -480,9 +496,14 @@ export class EntityRegistrySettingsEditor extends LitElement { domain === entry.domain ? nothing : html` - + ${entry.label} - + ` )} @@ -513,12 +534,13 @@ export class EntityRegistrySettingsEditor extends LitElement { .label=${this.hass.localize( "ui.dialogs.entity_registry.editor.device_class" )} - .value=${this._deviceClass} - naturalMenuWidth - fixedMenuPosition + .value=${this._deviceClass + ? this.hass.localize( + `ui.dialogs.entity_registry.editor.device_classes.${domain}.${this._deviceClass}` + ) + : undefined} clearable @selected=${this._deviceClassChanged} - @closed=${stopPropagation} > ${this._deviceClassesSorted( domain, @@ -526,29 +548,39 @@ export class EntityRegistrySettingsEditor extends LitElement { this.hass.localize ).map( (entry) => html` - + ${entry.label} - + ` )} ${this._deviceClassOptions[0].length && this._deviceClassOptions[1].length - ? html`
  • ` - : ""} + ? html`` + : nothing} ${this._deviceClassesSorted( domain, this._deviceClassOptions[1], this.hass.localize ).map( (entry) => html` - + ${entry.label} - + ` )} ` - : ""} + : nothing} ${domain === "number" && this._deviceClass && stateObj?.attributes.unit_of_measurement && @@ -560,20 +592,14 @@ export class EntityRegistrySettingsEditor extends LitElement { .label=${this.hass.localize( "ui.dialogs.entity_registry.editor.unit_of_measurement" )} - .value=${stateObj.attributes.unit_of_measurement} - naturalMenuWidth - fixedMenuPosition + .value=${this._unit_of_measurement || + stateObj.attributes.unit_of_measurement} @selected=${this._unitChanged} - @closed=${stopPropagation} + .options=${this._numberDeviceClassConvertibleUnits} > - ${this._numberDeviceClassConvertibleUnits.map( - (unit: string) => html` - ${unit} - ` - )} ` - : ""} + : nothing} ${domain === "lock" ? html` ` - : ""} + : nothing} ${domain === "alarm_control_panel" ? html` ` - : ""} + : nothing} ${domain === "calendar" ? html` ` - : ""} + : nothing} ${domain === "sensor" && this._deviceClass && stateObj?.attributes.unit_of_measurement && @@ -628,20 +654,14 @@ export class EntityRegistrySettingsEditor extends LitElement { .label=${this.hass.localize( "ui.dialogs.entity_registry.editor.unit_of_measurement" )} - .value=${stateObj.attributes.unit_of_measurement} - naturalMenuWidth - fixedMenuPosition + .value=${this._unit_of_measurement || + stateObj.attributes.unit_of_measurement} @selected=${this._unitChanged} - @closed=${stopPropagation} + .options=${this._sensorDeviceClassConvertibleUnits} > - ${this._sensorDeviceClassConvertibleUnits.map( - (unit: string) => html` - ${unit} - ` - )} ` - : ""} + : nothing} ${domain === "sensor" && // Allow customizing the precision for a sensor with numerical device class, // a unit of measurement or state class @@ -657,116 +677,78 @@ export class EntityRegistrySettingsEditor extends LitElement { .value=${this._precision == null ? "default" : this._precision.toString()} - naturalMenuWidth - fixedMenuPosition @selected=${this._precisionChanged} - @closed=${stopPropagation} + .options=${[ + { + value: "default", + label: this.hass.localize( + "ui.dialogs.entity_registry.editor.precision_default", + { + value: this._precisionLabel( + defaultPrecision, + stateObj?.state + ), + } + ), + }, + ...PRECISIONS.map((precision) => ({ + value: precision.toString(), + label: this._precisionLabel(precision, stateObj?.state), + })), + ]} > - ${this.hass.localize( - "ui.dialogs.entity_registry.editor.precision_default", - { - value: this._precisionLabel( - defaultPrecision, - stateObj?.state - ), - } - )} - ${PRECISIONS.map( - (precision) => html` - - ${this._precisionLabel(precision, stateObj?.state)} - - ` - )} ` - : ""} + : nothing} ${domain === "weather" ? html` - ${this._weatherConvertibleUnits?.precipitation_unit.map( - (unit: string) => html` - ${unit} - ` - )} - ${this._weatherConvertibleUnits?.pressure_unit.map( - (unit: string) => html` - ${unit} - ` - )} - ${this._weatherConvertibleUnits?.temperature_unit.map( - (unit: string) => html` - ${unit} - ` - )} - ${this._weatherConvertibleUnits?.visibility_unit.map( - (unit: string) => html` - ${unit} - ` - )} - ${this._weatherConvertibleUnits?.wind_speed_unit.map( - (unit: string) => html` - ${unit} - ` - )} ` - : ""} + : nothing} ` - : ""} + : nothing} ({ + value: num.toString(), + label: this.hass.localize( + ("ui.dialogs.entity_registry.editor.stream.stream_orientation_" + + num.toString()) as LocalizeKeys + ), + }))} + .value=${this._cameraPrefs.orientation.toString()} > - ${CAMERA_ORIENTATIONS.map((num) => { - const localizeStr = - "ui.dialogs.entity_registry.editor.stream.stream_orientation_" + - num.toString(); - return html` - - ${this.hass.localize(localizeStr as LocalizeKeys)} - - `; - })} ` - : ""} + : nothing} ${this.helperConfigEntry && this.helperConfigEntry.supports_options && this.helperConfigEntry.domain !== "switch_as_x" @@ -899,7 +876,7 @@ export class EntityRegistrySettingsEditor extends LitElement { ` - : ""} + : nothing} ` - : ""} + : nothing} ` - : ""} + : nothing} ${this.hass.localize( @@ -1036,8 +1013,8 @@ export class EntityRegistrySettingsEditor extends LitElement { .disabled=${this.disabled} @value-changed=${this._areaPicked} >` - : ""} ` - : ""} + : nothing} ` + : nothing} `; } @@ -1343,14 +1320,14 @@ export class EntityRegistrySettingsEditor extends LitElement { this._entityId = `${computeDomain(this._origEntityId)}.${ev.target.value}`; } - private _deviceClassChanged(ev): void { + private _deviceClassChanged(ev: CustomEvent<{ value: string }>): void { fireEvent(this, "change"); - this._deviceClass = ev.target.value; + this._deviceClass = ev.detail.value; } - private _unitChanged(ev): void { + private _unitChanged(ev: CustomEvent<{ value: string }>): void { fireEvent(this, "change"); - this._unit_of_measurement = ev.target.value; + this._unit_of_measurement = ev.detail.value; } private _defaultcodeChanged(ev): void { @@ -1363,53 +1340,53 @@ export class EntityRegistrySettingsEditor extends LitElement { this._calendarColor = ev.detail.value || null; } - private _precipitationUnitChanged(ev): void { + private _precipitationUnitChanged(ev: CustomEvent<{ value: string }>): void { fireEvent(this, "change"); - this._precipitation_unit = ev.target.value; + this._precipitation_unit = ev.detail.value; } - private _precisionChanged(ev): void { + private _precisionChanged(ev: CustomEvent<{ value: string }>): void { fireEvent(this, "change"); this._precision = - ev.target.value === "default" ? null : Number(ev.target.value); + ev.detail.value === "default" ? null : Number(ev.detail.value); } - private _pressureUnitChanged(ev): void { + private _pressureUnitChanged(ev: CustomEvent<{ value: string }>): void { fireEvent(this, "change"); - this._pressure_unit = ev.target.value; + this._pressure_unit = ev.detail.value; } - private _temperatureUnitChanged(ev): void { + private _temperatureUnitChanged(ev: CustomEvent<{ value: string }>): void { fireEvent(this, "change"); - this._temperature_unit = ev.target.value; + this._temperature_unit = ev.detail.value; } - private _visibilityUnitChanged(ev): void { + private _visibilityUnitChanged(ev: CustomEvent<{ value: string }>): void { fireEvent(this, "change"); - this._visibility_unit = ev.target.value; + this._visibility_unit = ev.detail.value; } - private _windSpeedUnitChanged(ev): void { + private _windSpeedUnitChanged(ev: CustomEvent<{ value: string }>): void { fireEvent(this, "change"); - this._wind_speed_unit = ev.target.value; + this._wind_speed_unit = ev.detail.value; } - private _switchAsDomainChanged(ev): void { - if (ev.target.value === "") { + private _switchAsDomainChanged(ev: CustomEvent<{ value: string }>): void { + const value = ev.detail.value; + if (value === "") { return; } // If value is "outlet" that means the user kept the "switch" domain, but actually changed // the device_class of the switch to "outlet". - const switchAs = ev.target.value === "outlet" ? "switch" : ev.target.value; - this._switchAsDomain = switchAs; + this._switchAsDomain = value === "outlet" ? "switch" : value; if ( (computeDomain(this.entry.entity_id) === "switch" && - ev.target.value === "outlet") || - ev.target.value === "switch" + value === "outlet") || + value === "switch" ) { - this._deviceClass = ev.target.value; + this._deviceClass = value; } } @@ -1466,13 +1443,15 @@ export class EntityRegistrySettingsEditor extends LitElement { } } - private async _handleCameraOrientationChanged(ev) { + private async _handleCameraOrientationChanged( + ev: CustomEvent<{ value: string }> + ) { try { this._cameraPrefs = await updateCameraPrefs( this.hass, this.entry.entity_id, { - orientation: ev.currentTarget.value, + orientation: Number(ev.detail.value), } ); } catch (err: any) { @@ -1547,6 +1526,35 @@ export class EntityRegistrySettingsEditor extends LitElement { ) ); + private _switchAsLabel = memoizeOne( + (switchAsDomain: string, deviceClass?: string) => { + if (switchAsDomain !== "switch") { + const switchAsDomains = this._switchAsDomainsSorted( + SWITCH_AS_DOMAINS, + this.hass.localize + ); + + for (const entry of switchAsDomains) { + if (entry.domain === switchAsDomain) { + return entry.label; + } + } + } + + if (!deviceClass || deviceClass === "switch") { + return domainToName(this.hass.localize, "switch"); + } + + if (deviceClass === "outlet") { + return this.hass.localize( + "ui.dialogs.entity_registry.editor.device_classes.switch.outlet" + ); + } + + return switchAsDomain; + } + ); + static get styles(): CSSResultGroup { return [ haStyle, @@ -1592,9 +1600,6 @@ export class EntityRegistrySettingsEditor extends LitElement { margin: var(--ha-space-2) 0; width: 100%; } - li[divider] { - border-bottom-color: var(--divider-color); - } .menu-item { border-radius: var(--ha-border-radius-sm); margin-top: 3px; diff --git a/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts b/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts index 6ff2efad38..e6ae950c52 100644 --- a/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts +++ b/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts @@ -2,21 +2,20 @@ import type { CSSResultGroup, TemplateResult } from "lit"; import { css, html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; import { storage } from "../../../../../common/decorators/storage"; +import "../../../../../components/ha-button"; import "../../../../../components/ha-card"; import "../../../../../components/ha-code-editor"; import "../../../../../components/ha-formfield"; -import "../../../../../components/ha-list-item"; import "../../../../../components/ha-switch"; -import "../../../../../components/ha-button"; import { getConfigEntries } from "../../../../../data/config_entries"; +import type { Action } from "../../../../../data/script"; +import { callExecuteScript } from "../../../../../data/service"; import { showOptionsFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-options-flow"; import "../../../../../layouts/hass-subpage"; import { haStyle } from "../../../../../resources/styles"; import type { HomeAssistant } from "../../../../../types"; -import "./mqtt-subscribe-card"; -import type { Action } from "../../../../../data/script"; -import { callExecuteScript } from "../../../../../data/service"; import { showToast } from "../../../../../util/toast"; +import "./mqtt-subscribe-card"; const qosLevel = ["0", "1", "2"]; @@ -89,10 +88,8 @@ export class MQTTConfigPanel extends LitElement { .label=${this.hass.localize("ui.panel.config.mqtt.qos")} .value=${this._qos} @selected=${this._handleQos} - >${qosLevel.map( - (qos) => - html`${qos}` - )} + .options=${qosLevel} + > = 0 && newValue !== this._qos) { + private _handleQos(ev: CustomEvent<{ value: string }>) { + const newValue = ev.detail.value; + if (Number(newValue) >= 0 && newValue !== this._qos) { this._qos = newValue; } } diff --git a/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts b/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts index 489245fd41..0d2d7e9069 100644 --- a/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts +++ b/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts @@ -2,8 +2,8 @@ import type { TemplateResult } from "lit"; import { css, html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; import { formatTime } from "../../../../../common/datetime/format_time"; -import "../../../../../components/ha-card"; import "../../../../../components/ha-button"; +import "../../../../../components/ha-card"; import "../../../../../components/ha-select"; import "../../../../../components/ha-textfield"; import type { MQTTMessage } from "../../../../../data/mqtt"; @@ -12,7 +12,6 @@ import type { HomeAssistant } from "../../../../../types"; import { storage } from "../../../../../common/decorators/storage"; import "../../../../../components/ha-formfield"; -import "../../../../../components/ha-list-item"; import "../../../../../components/ha-switch"; const qosLevel = ["0", "1", "2"]; @@ -96,9 +95,8 @@ class MqttSubscribeCard extends LitElement { .disabled=${this._subscribed !== undefined} .value=${this._qos} @selected=${this._handleQos} - >${qosLevel.map( - (qos) => html`${qos}` - )} + .options=${qosLevel} + > = 0 && newValue !== this._qos) { + private _handleQos(ev: CustomEvent<{ value: string }>): void { + const newValue = ev.detail.value; + if (Number(newValue) >= 0 && newValue !== this._qos) { this._qos = newValue; } } @@ -193,8 +191,8 @@ class MqttSubscribeCard extends LitElement { static styles = css` form { - display: block; - padding: 16px; + padding: var(--ha-space-4); + padding-bottom: var(--ha-space-8); } .events { margin: -16px 0; @@ -229,6 +227,7 @@ class MqttSubscribeCard extends LitElement { } @media screen and (max-width: 600px) { ha-select { + display: block; margin-left: 0px; margin-top: 8px; margin-inline-start: 0px; diff --git a/src/panels/config/integrations/integration-panels/zha/dialog-zha-change-channel.ts b/src/panels/config/integrations/integration-panels/zha/dialog-zha-change-channel.ts index 58b1a13dd8..0679a5c1d5 100644 --- a/src/panels/config/integrations/integration-panels/zha/dialog-zha-change-channel.ts +++ b/src/panels/config/integrations/integration-panels/zha/dialog-zha-change-channel.ts @@ -2,12 +2,10 @@ import type { TemplateResult } from "lit"; import { html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../../../common/dom/fire_event"; -import { stopPropagation } from "../../../../../common/dom/stop_propagation"; import "../../../../../components/buttons/ha-progress-button"; import "../../../../../components/ha-alert"; import "../../../../../components/ha-button"; import { createCloseHeading } from "../../../../../components/ha-dialog"; -import "../../../../../components/ha-list-item"; import "../../../../../components/ha-select"; import { changeZHANetworkChannel } from "../../../../../data/zha"; import { showAlertDialog } from "../../../../../dialogs/generic/show-dialog-box"; @@ -96,22 +94,18 @@ class DialogZHAChangeChannel extends LitElement implements HassDialog { .label=${this.hass.localize( "ui.panel.config.zha.change_channel_dialog.new_channel" )} - fixedMenuPosition - naturalMenuWidth @selected=${this._newChannelChosen} - @closed=${stopPropagation} .value=${String(this._newChannel)} + .options=${VALID_CHANNELS.map((channel) => ({ + value: String(channel), + label: + channel === "auto" + ? this.hass.localize( + "ui.panel.config.zha.change_channel_dialog.channel_auto" + ) + : String(channel), + }))} > - ${VALID_CHANNELS.map( - (newChannel) => - html`${newChannel === "auto" - ? this.hass.localize( - "ui.panel.config.zha.change_channel_dialog.channel_auto" - ) - : newChannel}` - )}

    @@ -137,8 +131,8 @@ class DialogZHAChangeChannel extends LitElement implements HassDialog { `; } - private _newChannelChosen(evt: Event): void { - const value: string = (evt.target! as HTMLSelectElement).value; + private _newChannelChosen(ev: CustomEvent<{ value: string }>): void { + const value: string = ev.detail.value; this._newChannel = value === "auto" ? "auto" : parseInt(value, 10); } diff --git a/src/panels/config/integrations/integration-panels/zha/zha-cluster-attributes.ts b/src/panels/config/integrations/integration-panels/zha/zha-cluster-attributes.ts index fed4bd7589..cce81a9452 100644 --- a/src/panels/config/integrations/integration-panels/zha/zha-cluster-attributes.ts +++ b/src/panels/config/integrations/integration-panels/zha/zha-cluster-attributes.ts @@ -1,11 +1,9 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { stopPropagation } from "../../../../../common/dom/stop_propagation"; import "../../../../../components/buttons/ha-call-service-button"; import "../../../../../components/buttons/ha-progress-button"; import "../../../../../components/ha-card"; -import "../../../../../components/ha-list-item"; import "../../../../../components/ha-select"; import "../../../../../components/ha-textfield"; import { forwardHaptic } from "../../../../../data/haptics"; @@ -22,7 +20,7 @@ import { import { haStyle } from "../../../../../resources/styles"; import type { HomeAssistant } from "../../../../../types"; import { formatAsPaddedHex } from "./functions"; -import type { ItemSelectedEvent, SetAttributeServiceData } from "./types"; +import type { SetAttributeServiceData } from "./types"; @customElement("zha-cluster-attributes") export class ZHAClusterAttributes extends LitElement { @@ -70,22 +68,16 @@ export class ZHAClusterAttributes extends LitElement { class="menu" .value=${String(this._selectedAttributeId)} @selected=${this._selectedAttributeChanged} - @closed=${stopPropagation} - fixedMenuPosition - naturalMenuWidth + .options=${this._attributes.map((entry) => ({ + value: String(entry.id), + label: `${entry.name} (id: ${formatAsPaddedHex(entry.id)})`, + }))} > - ${this._attributes.map( - (entry) => html` - - ${`${entry.name} (id: ${formatAsPaddedHex(entry.id)})`} - - ` - )} ${this._selectedAttributeId !== undefined ? this._renderAttributeInteractions() - : ""} + : nothing} `; } @@ -221,8 +213,10 @@ export class ZHAClusterAttributes extends LitElement { } } - private _selectedAttributeChanged(event: ItemSelectedEvent): void { - this._selectedAttributeId = Number(event.target!.value); + private _selectedAttributeChanged( + event: CustomEvent<{ value: string }> + ): void { + this._selectedAttributeId = Number(event.detail.value); this._attributeValue = ""; } diff --git a/src/panels/config/integrations/integration-panels/zha/zha-cluster-commands.ts b/src/panels/config/integrations/integration-panels/zha/zha-cluster-commands.ts index 688a8588cb..c8cfd8cc5b 100644 --- a/src/panels/config/integrations/integration-panels/zha/zha-cluster-commands.ts +++ b/src/panels/config/integrations/integration-panels/zha/zha-cluster-commands.ts @@ -1,11 +1,9 @@ import type { CSSResultGroup, PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { stopPropagation } from "../../../../../common/dom/stop_propagation"; import "../../../../../components/buttons/ha-call-service-button"; import "../../../../../components/ha-card"; import "../../../../../components/ha-form/ha-form"; -import "../../../../../components/ha-list-item"; import "../../../../../components/ha-select"; import "../../../../../components/ha-textfield"; import type { Cluster, Command, ZHADevice } from "../../../../../data/zha"; @@ -64,17 +62,11 @@ export class ZHAClusterCommands extends LitElement { class="menu" .value=${String(this._selectedCommandId)} @selected=${this._selectedCommandChanged} - @closed=${stopPropagation} - fixedMenuPosition - naturalMenuWidth + .options=${this._commands.map((entry) => ({ + value: String(entry.id), + label: `${entry.name} (id: ${formatAsPaddedHex(entry.id)})`, + }))} > - ${this._commands.map( - (entry) => html` - - ${entry.name} (id: ${formatAsPaddedHex(entry.id)}) - - ` - )} ${this._selectedCommandId !== undefined @@ -179,8 +171,8 @@ export class ZHAClusterCommands extends LitElement { this._computeIssueClusterCommandServiceData(); } - private _selectedCommandChanged(event): void { - this._selectedCommandId = Number(event.target.value); + private _selectedCommandChanged(event: CustomEvent<{ value: string }>): void { + this._selectedCommandId = Number(event.detail.value); this._issueClusterCommandServiceData = this._computeIssueClusterCommandServiceData(); } diff --git a/src/panels/config/integrations/integration-panels/zha/zha-device-binding.ts b/src/panels/config/integrations/integration-panels/zha/zha-device-binding.ts index f2e17027ed..98507d8597 100644 --- a/src/panels/config/integrations/integration-panels/zha/zha-device-binding.ts +++ b/src/panels/config/integrations/integration-panels/zha/zha-device-binding.ts @@ -1,16 +1,13 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { css, html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { stopPropagation } from "../../../../../common/dom/stop_propagation"; import "../../../../../components/buttons/ha-progress-button"; import "../../../../../components/ha-card"; import "../../../../../components/ha-select"; -import "../../../../../components/ha-list-item"; import type { ZHADevice } from "../../../../../data/zha"; import { bindDevices, unbindDevices } from "../../../../../data/zha"; import { haStyle } from "../../../../../resources/styles"; import type { HomeAssistant } from "../../../../../types"; -import type { ItemSelectedEvent } from "./types"; @customElement("zha-device-binding-control") export class ZHADeviceBindingControl extends LitElement { @@ -44,19 +41,13 @@ export class ZHADeviceBindingControl extends LitElement { class="menu" .value=${String(this._bindTargetIndex)} @selected=${this._bindTargetIndexChanged} - @closed=${stopPropagation} - fixedMenuPosition - naturalMenuWidth + .options=${this.bindableDevices.map((device, idx) => ({ + value: String(idx), + label: device.user_given_name + ? device.user_given_name + : device.name, + }))} > - ${this.bindableDevices.map( - (device, idx) => html` - - ${device.user_given_name - ? device.user_given_name - : device.name} - - ` - )}
    @@ -81,8 +72,8 @@ export class ZHADeviceBindingControl extends LitElement { `; } - private _bindTargetIndexChanged(event: ItemSelectedEvent): void { - this._bindTargetIndex = Number(event.target!.value); + private _bindTargetIndexChanged(event: CustomEvent<{ value: string }>): void { + this._bindTargetIndex = Number(event.detail.value); this._deviceToBind = this._bindTargetIndex === -1 ? undefined diff --git a/src/panels/config/integrations/integration-panels/zha/zha-group-binding.ts b/src/panels/config/integrations/integration-panels/zha/zha-group-binding.ts index 59142d155a..3b57f46237 100644 --- a/src/panels/config/integrations/integration-panels/zha/zha-group-binding.ts +++ b/src/panels/config/integrations/integration-panels/zha/zha-group-binding.ts @@ -1,13 +1,11 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { css, html, LitElement } from "lit"; -import { customElement, property, state, query } from "lit/decorators"; +import { customElement, property, query, state } from "lit/decorators"; import type { HASSDomEvent } from "../../../../../common/dom/fire_event"; -import { stopPropagation } from "../../../../../common/dom/stop_propagation"; import "../../../../../components/buttons/ha-progress-button"; import type { SelectionChangedEvent } from "../../../../../components/data-table/ha-data-table"; import "../../../../../components/ha-card"; import "../../../../../components/ha-select"; -import "../../../../../components/ha-list-item"; import type { Cluster, ZHADevice, ZHAGroup } from "../../../../../data/zha"; import { bindDeviceToGroup, @@ -16,7 +14,6 @@ import { } from "../../../../../data/zha"; import { haStyle } from "../../../../../resources/styles"; import type { HomeAssistant } from "../../../../../types"; -import type { ItemSelectedEvent } from "./types"; import "./zha-clusters-data-table"; import type { ZHAClustersDataTable } from "./zha-clusters-data-table"; @@ -64,16 +61,11 @@ export class ZHAGroupBindingControl extends LitElement { class="menu" .value=${String(this._bindTargetIndex)} @selected=${this._bindTargetIndexChanged} - @closed=${stopPropagation} - fixedMenuPosition - naturalMenuWidth + .options=${this.groups.map((group, idx) => ({ + value: String(idx), + label: group.name, + }))} > - ${this.groups.map( - (group, idx) => - html`${group.name} ` - )}
    @@ -109,8 +101,8 @@ export class ZHAGroupBindingControl extends LitElement { `; } - private _bindTargetIndexChanged(event: ItemSelectedEvent): void { - this._bindTargetIndex = Number(event.target!.value); + private _bindTargetIndexChanged(event: CustomEvent<{ value: string }>): void { + this._bindTargetIndex = Number(event.detail.value); this._groupToBind = this._bindTargetIndex === -1 ? undefined diff --git a/src/panels/config/integrations/integration-panels/zha/zha-manage-clusters.ts b/src/panels/config/integrations/integration-panels/zha/zha-manage-clusters.ts index 8f8cc46c70..814345de70 100644 --- a/src/panels/config/integrations/integration-panels/zha/zha-manage-clusters.ts +++ b/src/panels/config/integrations/integration-panels/zha/zha-manage-clusters.ts @@ -2,9 +2,7 @@ import type { CSSResultGroup, PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { cache } from "lit/directives/cache"; -import { stopPropagation } from "../../../../../common/dom/stop_propagation"; import "../../../../../components/ha-card"; -import "../../../../../components/ha-list-item"; import "../../../../../components/ha-select"; import "../../../../../components/ha-tab-group"; import "../../../../../components/ha-tab-group-tab"; @@ -77,17 +75,11 @@ export class ZHAManageClusters extends LitElement { class="menu" .value=${String(this._selectedClusterIndex)} @selected=${this._selectedClusterChanged} - @closed=${stopPropagation} - fixedMenuPosition - naturalMenuWidth + .options=${this._clusters.map((entry, idx) => ({ + value: String(idx), + label: computeClusterKey(entry), + }))} > - ${this._clusters.map( - (entry, idx) => html` - ${computeClusterKey(entry)} - ` - )}
    ${this._selectedCluster @@ -155,8 +147,8 @@ export class ZHAManageClusters extends LitElement { this._currTab = newTab; } - private _selectedClusterChanged(event): void { - this._selectedClusterIndex = Number(event.target!.value); + private _selectedClusterChanged(event: CustomEvent<{ value: string }>): void { + this._selectedClusterIndex = Number(event.detail.value); this._selectedCluster = this._clusters[this._selectedClusterIndex]; } diff --git a/src/panels/config/integrations/integration-panels/zwave_js/capability-controls/zwave_js-capability-control-door-lock.ts b/src/panels/config/integrations/integration-panels/zwave_js/capability-controls/zwave_js-capability-control-door-lock.ts index 530d7b17b8..b1f99beb6c 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/capability-controls/zwave_js-capability-control-door-lock.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/capability-controls/zwave_js-capability-control-door-lock.ts @@ -5,7 +5,6 @@ import type { HaProgressButton } from "../../../../../../components/buttons/ha-p import "../../../../../../components/ha-alert"; import "../../../../../../components/ha-button"; import "../../../../../../components/ha-formfield"; -import "../../../../../../components/ha-list-item"; import "../../../../../../components/ha-select"; import "../../../../../../components/ha-spinner"; import "../../../../../../components/ha-switch"; @@ -126,16 +125,13 @@ class ZWaveJSCapabilityDoorLock extends LitElement { )} .value=${this._currentDoorLockMode?.toString() ?? ""} @selected=${this._doorLockModeChanged} + .options=${supportedDoorLockModes.map((mode) => ({ + value: mode.toString(), + label: this.hass.localize( + `ui.panel.config.zwave_js.node_installer.capability_controls.door_lock.modes.${mode}` + ), + }))} > - ${supportedDoorLockModes.map( - (mode) => html` - - ${this.hass.localize( - `ui.panel.config.zwave_js.node_installer.capability_controls.door_lock.modes.${mode}` - )} - - ` - )}
    @@ -145,16 +141,13 @@ class ZWaveJSCapabilityDoorLock extends LitElement { )} .value=${this._configuration.operationType.toString()} @selected=${this._operationTypeChanged} + .options=${this._capabilities.supportedOperationTypes.map((type) => ({ + value: type.toString(), + label: this.hass.localize( + `ui.panel.config.zwave_js.node_installer.capability_controls.door_lock.operation_types.${type}` + ), + }))} > - ${this._capabilities.supportedOperationTypes.map( - (type) => html` - - ${this.hass.localize( - `ui.panel.config.zwave_js.node_installer.capability_controls.door_lock.operation_types.${type}` - )} - - ` - )}
    @@ -346,9 +339,8 @@ class ZWaveJSCapabilityDoorLock extends LitElement { ); } - private _operationTypeChanged(ev: CustomEvent) { - const target = ev.target as HTMLSelectElement; - const newType = parseInt(target.value); + private _operationTypeChanged(ev: CustomEvent<{ value: string }>) { + const newType = parseInt(ev.detail.value); if (this._configuration) { this._configuration = { ...this._configuration, @@ -393,9 +385,8 @@ class ZWaveJSCapabilityDoorLock extends LitElement { } } - private _doorLockModeChanged(ev: CustomEvent) { - const target = ev.target as HTMLSelectElement; - this._currentDoorLockMode = parseInt(target.value) as DoorLockMode; + private _doorLockModeChanged(ev: CustomEvent<{ value: string }>) { + this._currentDoorLockMode = parseInt(ev.detail.value) as DoorLockMode; } private async _saveConfig(ev: CustomEvent) { diff --git a/src/panels/config/integrations/integration-panels/zwave_js/capability-controls/zwave_js-capability-control-multilevel-switch.ts b/src/panels/config/integrations/integration-panels/zwave_js/capability-controls/zwave_js-capability-control-multilevel-switch.ts index 60371c449f..8ba633b078 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/capability-controls/zwave_js-capability-control-multilevel-switch.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/capability-controls/zwave_js-capability-control-multilevel-switch.ts @@ -1,12 +1,10 @@ -import { LitElement, css, html } from "lit"; +import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import "../../../../../../components/buttons/ha-progress-button"; import type { HaProgressButton } from "../../../../../../components/buttons/ha-progress-button"; import "../../../../../../components/ha-alert"; import "../../../../../../components/ha-formfield"; -import "../../../../../../components/ha-list-item"; import "../../../../../../components/ha-select"; -import type { HaSelect } from "../../../../../../components/ha-select"; import "../../../../../../components/ha-switch"; import type { HaSwitch } from "../../../../../../components/ha-switch"; import "../../../../../../components/ha-textfield"; @@ -35,6 +33,8 @@ class ZWaveJSCapabilityMultiLevelSwitch extends LitElement { @state() private _error?: string; + @state() private _direction = "up"; + protected render() { return html`

    @@ -44,23 +44,28 @@ class ZWaveJSCapabilityMultiLevelSwitch extends LitElement {

    ${this._error ? html`${this._error}` - : ""} + : nothing} - ${this.hass.localize( - "ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.up" - )} - ${this.hass.localize( - "ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.down" - )} ) { + this._direction = ev.detail.value; + } + static styles = css` ha-select, ha-formfield, diff --git a/src/panels/config/integrations/integration-panels/zwave_js/capability-controls/zwave_js-capability-control-thermostat-setback.ts b/src/panels/config/integrations/integration-panels/zwave_js/capability-controls/zwave_js-capability-control-thermostat-setback.ts index 53c7689ad2..152cce7941 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/capability-controls/zwave_js-capability-control-thermostat-setback.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/capability-controls/zwave_js-capability-control-thermostat-setback.ts @@ -1,12 +1,10 @@ -import { LitElement, css, html } from "lit"; +import { LitElement, css, html, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import "../../../../../../components/buttons/ha-progress-button"; import type { HaProgressButton } from "../../../../../../components/buttons/ha-progress-button"; import "../../../../../../components/ha-alert"; import "../../../../../../components/ha-button"; -import "../../../../../../components/ha-list-item"; import "../../../../../../components/ha-select"; -import type { HaSelect } from "../../../../../../components/ha-select"; import "../../../../../../components/ha-textfield"; import type { HaTextField } from "../../../../../../components/ha-textfield"; import type { DeviceRegistryEntry } from "../../../../../../data/device/device_registry"; @@ -37,13 +35,12 @@ class ZWaveJSCapabilityThermostatSetback extends LitElement { @state() private _disableSetbackState = false; - @query("#setback_type") private _setbackTypeInput!: HaSelect; + @state() private _setbackType = "0"; + + @state() private _setbackSpecialType?: string; @query("#setback_state") private _setbackStateInput!: HaTextField; - @query("#setback_special_state") - private _setbackSpecialStateSelect!: HaSelect; - @state() private _error?: string; @state() private _loading = true; @@ -57,23 +54,21 @@ class ZWaveJSCapabilityThermostatSetback extends LitElement { ${this._error ? html`${this._error}` - : ""} + : nothing} ({ + value: String(index), + label: this.hass.localize( + `ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_type.${translationKey}` + ), + }))} > - ${SETBACK_TYPE_OPTIONS.map( - (translationKey, index) => - html` - ${this.hass.localize( - `ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_type.${translationKey}` - )} - ` - )}
    - - ${Object.entries(SpecialState).map( - ([translationKey, value]) => - html` - ${this.hass.localize( - `ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_special_state.${translationKey}` - )} - ` + .value=${this._setbackSpecialType} + clearable + .options=${Object.entries(SpecialState).map( + ([translationKey, value]) => ({ + value: value, + label: this.hass.localize( + `ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_special_state.${translationKey}` + ), + }) )} + >
    @@ -144,12 +140,12 @@ class ZWaveJSCapabilityThermostatSetback extends LitElement { true )) as { setbackType: number; setbackState: number | SpecialState }; - this._setbackTypeInput.value = String(setbackType); + this._setbackType = String(setbackType); if (typeof setbackState === "number") { this._setbackStateInput.value = String(setbackState); - this._setbackSpecialStateSelect.value = ""; + this._setbackSpecialType = undefined; } else { - this._setbackSpecialStateSelect.value = setbackState; + this._setbackSpecialType = setbackState; } } catch (err) { this._error = this.hass.localize( @@ -161,8 +157,9 @@ class ZWaveJSCapabilityThermostatSetback extends LitElement { this._loading = false; } - private _changeSpecialState() { - this._disableSetbackState = !!this._setbackSpecialStateSelect.value; + private _changeSpecialState(ev: CustomEvent<{ value: string }>) { + this._disableSetbackState = !ev.detail.value; + this._setbackSpecialType = ev.detail.value; } private async _saveSetback(ev: CustomEvent) { @@ -170,11 +167,11 @@ class ZWaveJSCapabilityThermostatSetback extends LitElement { button.progress = true; this._error = undefined; - const setbackType = this._setbackTypeInput.value; + const setbackType = this._setbackType; let setbackState: number | string = Number(this._setbackStateInput.value); - if (this._setbackSpecialStateSelect.value) { - setbackState = this._setbackSpecialStateSelect.value; + if (this._setbackSpecialType) { + setbackState = this._setbackSpecialType; } try { @@ -204,6 +201,10 @@ class ZWaveJSCapabilityThermostatSetback extends LitElement { this._loadSetback(); } + private _setbackTypeChanged(ev: CustomEvent<{ value: string }>) { + this._setbackType = ev.detail.value; + } + static styles = css` :host { display: flex; diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-custom-param.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-custom-param.ts index 948864acf0..3002ebb335 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-custom-param.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-custom-param.ts @@ -1,17 +1,16 @@ -import { LitElement, html, css, nothing } from "lit"; -import { customElement, property, state } from "lit/decorators"; import { mdiCloseCircle } from "@mdi/js"; -import "../../../../../components/ha-textfield"; -import "../../../../../components/ha-select"; +import { LitElement, css, html, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { fireEvent } from "../../../../../common/dom/fire_event"; import "../../../../../components/ha-button"; +import "../../../../../components/ha-select"; import "../../../../../components/ha-spinner"; -import "../../../../../components/ha-list-item"; -import type { HomeAssistant } from "../../../../../types"; +import "../../../../../components/ha-textfield"; import { getZwaveNodeRawConfigParameter, setZwaveNodeRawConfigParameter, } from "../../../../../data/zwave_js"; -import { fireEvent } from "../../../../../common/dom/fire_event"; +import type { HomeAssistant } from "../../../../../types"; @customElement("zwave_js-custom-param") class ZWaveJSCustomParam extends LitElement { @@ -48,10 +47,8 @@ class ZWaveJSCustomParam extends LitElement { )} .value=${String(this._valueSize)} @selected=${this._customValueSizeChanged} + .options=${["1", "2", "4"]} > - 1 - 2 - 4 - ${this.hass.localize( - "ui.panel.config.zwave_js.node_config.signed" - )} - ${this.hass.localize( - "ui.panel.config.zwave_js.node_config.unsigned" - )} - ${this.hass.localize( - "ui.panel.config.zwave_js.node_config.enumerated" - )} - ${this.hass.localize( - "ui.panel.config.zwave_js.node_config.bitfield" - )}
    @@ -129,18 +132,16 @@ class ZWaveJSCustomParam extends LitElement { ); } - private _customValueSizeChanged(ev: Event) { - this._valueSize = - this._tryParseNumber((ev.target as HTMLSelectElement).value) ?? 1; + private _customValueSizeChanged(ev: CustomEvent<{ value: string }>) { + this._valueSize = this._tryParseNumber(ev.detail.value) ?? 1; } private _customValueChanged(ev: Event) { this._value = this._tryParseNumber((ev.target as HTMLInputElement).value); } - private _customValueFormatChanged(ev: Event) { - this._valueFormat = - this._tryParseNumber((ev.target as HTMLSelectElement).value) ?? 0; + private _customValueFormatChanged(ev: CustomEvent<{ value: string }>) { + this._valueFormat = this._tryParseNumber(ev.detail.value) ?? 0; } private async _getCustomConfigValue() { diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts index 9621277490..a327cd2369 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts @@ -5,7 +5,6 @@ import { css, html, LitElement } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { capitalizeFirstLetter } from "../../../../../common/string/capitalize-first-letter"; import "../../../../../components/ha-icon-button"; -import "../../../../../components/ha-list-item"; import "../../../../../components/ha-select"; import type { ZWaveJSLogConfig } from "../../../../../data/zwave_js"; import { @@ -84,13 +83,18 @@ class ZWaveJSLogs extends SubscribeMixin(LitElement) { )} .value=${this._logConfig.level} @selected=${this._dropdownSelected} + .options=${[ + "error", + "warn", + "info", + "verbose", + "debug", + "silly", + ].map((level) => ({ + value: level, + label: capitalizeFirstLetter(level), + }))} > - Error - Warn - Info - Verbose - Debug - Silly ` : ""} @@ -133,11 +137,11 @@ class ZWaveJSLogs extends SubscribeMixin(LitElement) { ); } - private _dropdownSelected(ev) { + private _dropdownSelected(ev: CustomEvent<{ value: string }>) { if (ev.target === undefined || this._logConfig === undefined) { return; } - const selected = ev.target.value; + const selected = ev.detail.value; if (this._logConfig.level === selected) { return; } diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts index de215f4fc8..eedea0ab47 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts @@ -17,7 +17,6 @@ import type { HaProgressButton } from "../../../../../components/buttons/ha-prog import "../../../../../components/ha-alert"; import "../../../../../components/ha-card"; import "../../../../../components/ha-generic-picker"; -import "../../../../../components/ha-list-item"; import type { PickerComboBoxItem } from "../../../../../components/ha-picker-combo-box"; import "../../../../../components/ha-select"; import "../../../../../components/ha-selector/ha-selector-boolean"; @@ -374,7 +373,6 @@ class ZWaveJSNodeConfig extends LitElement { return html` ${labelAndDescription} - ${Object.entries(item.metadata.states).map( - ([key, entityState]) => html` - ${entityState} - ` + .options=${Object.entries(item.metadata.states).map( + ([key, entityState]) => ({ + value: key, + label: entityState, + }) )} + > `; } @@ -457,8 +456,8 @@ class ZWaveJSNodeConfig extends LitElement { this._updateConfigParameter(ev.target, ev.detail.value ? 1 : 0); } - private _dropdownSelected(ev) { - this._handleEnumeratedPickerValueChanged(ev, ev.target.value); + private _dropdownSelected(ev: CustomEvent<{ value: string }>) { + this._handleEnumeratedPickerValueChanged(ev, ev.detail.value); } private _pickerValueChanged(ev) { @@ -469,7 +468,7 @@ class ZWaveJSNodeConfig extends LitElement { if (ev.target === undefined || this._config![ev.target.key] === undefined) { return; } - if (this._config![ev.target.key].value?.toString() === value) { + if (this._config![ev.target.key].value === value) { return; } this._setResult(ev.target.key, undefined); diff --git a/src/panels/config/storage/dialog-move-datadisk.ts b/src/panels/config/storage/dialog-move-datadisk.ts index b6ff482365..aabb280814 100644 --- a/src/panels/config/storage/dialog-move-datadisk.ts +++ b/src/panels/config/storage/dialog-move-datadisk.ts @@ -3,10 +3,8 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../common/dom/fire_event"; -import { stopPropagation } from "../../../common/dom/stop_propagation"; -import "../../../components/ha-dialog"; import "../../../components/ha-button"; -import "../../../components/ha-list-item"; +import "../../../components/ha-dialog"; import "../../../components/ha-select"; import "../../../components/ha-spinner"; import { @@ -132,26 +130,18 @@ class MoveDatadiskDialog extends LitElement { "ui.panel.config.storage.datadisk.select_device" )} @selected=${this._selectDevice} - @closed=${stopPropagation} - dialogInitialFocus - fixedMenuPosition - > - ${this._disks.map( - (disk) => - html` - ${disk.vendor} ${disk.model} - - ${this.hass.localize( - "ui.panel.config.storage.datadisk.extra_information", - { - size: bytesToString(disk.size), - serial: disk.serial, - } - )} - - ` - )} - + .options=${this._disks.map((disk) => ({ + value: disk.id, + label: `${disk.vendor} ${disk.model}`, + secondary: this.hass.localize( + "ui.panel.config.storage.datadisk.extra_information", + { + size: bytesToString(disk.size), + serial: disk.serial, + } + ), + }))} + > ): void { + this._selectedDevice = ev.detail.value; } private async _moveDatadisk() { diff --git a/src/panels/lovelace/components/hui-action-editor.ts b/src/panels/lovelace/components/hui-action-editor.ts index dbac50d2d0..2dcfdeb33d 100644 --- a/src/panels/lovelace/components/hui-action-editor.ts +++ b/src/panels/lovelace/components/hui-action-editor.ts @@ -1,19 +1,15 @@ -import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; -import { customElement, property, query } from "lit/decorators"; -import { refine } from "superstruct"; +import { customElement, property } from "lit/decorators"; import memoizeOne from "memoize-one"; +import { refine } from "superstruct"; import { fireEvent } from "../../../common/dom/fire_event"; -import { stopPropagation } from "../../../common/dom/stop_propagation"; import "../../../components/ha-assist-pipeline-picker"; import type { HaFormSchema, SchemaUnion, } from "../../../components/ha-form/types"; import "../../../components/ha-help-tooltip"; -import "../../../components/ha-list-item"; import "../../../components/ha-navigation-picker"; -import type { HaSelect } from "../../../components/ha-select"; import "../../../components/ha-service-control"; import type { ActionConfig, @@ -92,8 +88,6 @@ export class HuiActionEditor extends LitElement { @property({ attribute: false }) public context?: ActionRelatedContext; - @query("ha-select") private _select!: HaSelect; - get _navigation_path(): string { const config = this.config as NavigateActionConfig | undefined; return config?.navigation_path || ""; @@ -136,15 +130,6 @@ export class HuiActionEditor extends LitElement { ] ); - protected updated(changedProperties: PropertyValues) { - super.updated(changedProperties); - if (changedProperties.has("defaultAction")) { - if (changedProperties.get("defaultAction") !== this.defaultAction) { - this._select.layoutOptions(); - } - } - } - protected render() { if (!this.hass) { return nothing; @@ -165,29 +150,28 @@ export class HuiActionEditor extends LitElement { .configValue=${"action"} @selected=${this._actionPicked} .value=${action} - @closed=${stopPropagation} - fixedMenuPosition - naturalMenuWidth + .options=${[ + { + value: "default", + label: `${this.hass!.localize( + "ui.panel.lovelace.editor.action-editor.actions.default_action" + )} + ${ + this.defaultAction + ? ` (${this.hass!.localize( + `ui.panel.lovelace.editor.action-editor.actions.${this.defaultAction}` + ).toLowerCase()})` + : "" + }`, + }, + ...actions.map((actn) => ({ + value: actn, + label: this.hass!.localize( + `ui.panel.lovelace.editor.action-editor.actions.${actn}` + ), + })), + ]} > - - ${this.hass!.localize( - "ui.panel.lovelace.editor.action-editor.actions.default_action" - )} - ${this.defaultAction - ? ` (${this.hass!.localize( - `ui.panel.lovelace.editor.action-editor.actions.${this.defaultAction}` - ).toLowerCase()})` - : nothing} - - ${actions.map( - (actn) => html` - - ${this.hass!.localize( - `ui.panel.lovelace.editor.action-editor.actions.${actn}` - )} - - ` - )} ${this.tooltipText ? html` @@ -249,7 +233,7 @@ export class HuiActionEditor extends LitElement { `; } - private _actionPicked(ev): void { + private _actionPicked(ev: CustomEvent<{ value: string }>): void { ev.stopPropagation(); if (!this.hass) { return; @@ -260,7 +244,7 @@ export class HuiActionEditor extends LitElement { action = "perform-action"; } - const value = ev.target.value; + const value = ev.detail.value; if (action === value) { return; diff --git a/src/panels/lovelace/editor/hui-picture-elements-card-row-editor.ts b/src/panels/lovelace/editor/hui-picture-elements-card-row-editor.ts index 0db7f1e74e..d285cf51e0 100644 --- a/src/panels/lovelace/editor/hui-picture-elements-card-row-editor.ts +++ b/src/panels/lovelace/editor/hui-picture-elements-card-row-editor.ts @@ -1,13 +1,18 @@ -import { mdiClose, mdiContentDuplicate, mdiPencil } from "@mdi/js"; +import { + mdiClose, + mdiContentDuplicate, + mdiPencil, + mdiPlaylistPlus, +} from "@mdi/js"; import deepClone from "deep-clone-simple"; import { LitElement, css, html, nothing } from "lit"; -import { customElement, property, query } from "lit/decorators"; +import { customElement, property } from "lit/decorators"; import { fireEvent } from "../../../common/dom/fire_event"; -import { stopPropagation } from "../../../common/dom/stop_propagation"; +import "../../../components/ha-button"; +import "../../../components/ha-dropdown"; +import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown"; +import "../../../components/ha-dropdown-item"; import "../../../components/ha-icon-button"; -import "../../../components/ha-list-item"; -import "../../../components/ha-select"; -import type { HaSelect } from "../../../components/ha-select"; import "../../../components/ha-svg-icon"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import type { HomeAssistant } from "../../../types"; @@ -47,8 +52,6 @@ export class HuiPictureElementsCardRowEditor extends LitElement { @property({ attribute: false }) public elements?: LovelaceElementConfig[]; - @query("ha-select") private _select!: HaSelect; - protected render() { if (!this.elements || !this.hass) { return nothing; @@ -104,26 +107,23 @@ export class HuiPictureElementsCardRowEditor extends LitElement {
    ` )} - + + + + ${this.hass.localize( + "ui.panel.lovelace.editor.card.picture-elements.new_element" + )} + ${elementTypes.map( (element) => html` - ${this.hass?.localize( + + ${this.hass?.localize( `ui.panel.lovelace.editor.card.picture-elements.element_types.${element}` - )} + ) || element} + ` )} - + `; } @@ -177,8 +177,8 @@ export class HuiPictureElementsCardRowEditor extends LitElement { return element.title ?? "Unknown type"; } - private async _addElement(ev): Promise { - const value = ev.target!.value; + private async _addElement(ev: HaDropdownSelectEvent): Promise { + const value = ev.detail.item.value; if (value === "") { return; } @@ -191,7 +191,6 @@ export class HuiPictureElementsCardRowEditor extends LitElement { ) ); fireEvent(this, "elements-changed", { elements: newElements }); - this._select.select(-1); } private _removeRow(ev: CustomEvent): void { @@ -269,10 +268,6 @@ export class HuiPictureElementsCardRowEditor extends LitElement { font-size: var(--ha-font-size-s); color: var(--secondary-text-color); } - - ha-select { - width: 100%; - } `; } diff --git a/src/panels/lovelace/editor/select-view/hui-dialog-select-view.ts b/src/panels/lovelace/editor/select-view/hui-dialog-select-view.ts index 5acea78670..7c92e0c62f 100644 --- a/src/panels/lovelace/editor/select-view/hui-dialog-select-view.ts +++ b/src/panels/lovelace/editor/select-view/hui-dialog-select-view.ts @@ -2,13 +2,11 @@ import type { CSSResultGroup } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, state } from "lit/decorators"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { stopPropagation } from "../../../../common/dom/stop_propagation"; import "../../../../components/ha-alert"; import "../../../../components/ha-button"; import { createCloseHeading } from "../../../../components/ha-dialog"; import "../../../../components/ha-icon"; import "../../../../components/ha-list"; -import "../../../../components/ha-list-item"; import "../../../../components/ha-radio-list-item"; import "../../../../components/ha-select"; import type { LovelaceConfig } from "../../../../data/lovelace/config/types"; @@ -82,29 +80,23 @@ export class HuiDialogSelectView extends LitElement { .disabled=${!this._dashboards.length} .value=${this._urlPath || defaultPanel} @selected=${this._dashboardChanged} - @closed=${stopPropagation} - fixedMenuPosition - naturalMenuWidth + .options=${this._dashboards + .map((dashboard) => ({ + value: dashboard.url_path, + label: `${dashboard.title}${dashboard.id === "lovelace" ? ` (${this.hass.localize("ui.common.default")})` : ""}`, + disabled: dashboard.mode !== "storage", + })) + .sort((a, b) => + a.value === "lovelace" + ? -1 + : b.value === "lovelace" + ? 1 + : a.label.localeCompare(b.label) + )} dialogInitialFocus > - - Default - - ${this._dashboards.map( - (dashboard) => html` - ${dashboard.title} - ` - )} ` - : ""} + : nothing} ${!this._config || (this._config.views || []).length < 1 ? html`${this.hass.localize( @@ -142,7 +134,7 @@ export class HuiDialogSelectView extends LitElement { })} ` - : ""} + : nothing} ) { + let urlPath: string | null = ev.detail.value; if (urlPath === this._urlPath) { return; } diff --git a/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts index dfd8f7ec8d..6eb8b06969 100644 --- a/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts @@ -1,8 +1,6 @@ import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { stopPropagation } from "../../../common/dom/stop_propagation"; -import "../../../components/ha-list-item"; import "../../../components/ha-select"; import { UNAVAILABLE } from "../../../data/entity/entity"; import { forwardHaptic } from "../../../data/haptics"; @@ -70,17 +68,8 @@ class HuiInputSelectEntityRow extends LitElement implements LovelaceRow { .disabled=${ stateObj.state === UNAVAILABLE /* UNKNOWN state is allowed */ } - naturalMenuWidth @selected=${this._selectedChanged} - @click=${stopPropagation} - @closed=${stopPropagation} > - ${stateObj.attributes.options - ? stateObj.attributes.options.map( - (option) => - html`${option}` - ) - : ""} `; @@ -97,11 +86,11 @@ class HuiInputSelectEntityRow extends LitElement implements LovelaceRow { } `; - private _selectedChanged(ev): void { + private _selectedChanged(ev: CustomEvent<{ value: string }>): void { const stateObj = this.hass!.states[ this._config!.entity ] as InputSelectEntity; - const option = ev.target.value; + const option = ev.detail.value; if ( option === stateObj.state || !stateObj.attributes.options.includes(option) diff --git a/src/panels/lovelace/entity-rows/hui-select-entity-row.ts b/src/panels/lovelace/entity-rows/hui-select-entity-row.ts index 99e3f24cbb..fcdf36898c 100644 --- a/src/panels/lovelace/entity-rows/hui-select-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-select-entity-row.ts @@ -1,8 +1,6 @@ import type { PropertyValues } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { stopPropagation } from "../../../common/dom/stop_propagation"; -import "../../../components/ha-list-item"; import "../../../components/ha-select"; import { UNAVAILABLE } from "../../../data/entity/entity"; import { forwardHaptic } from "../../../data/haptics"; @@ -22,6 +20,8 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow { @state() private _config?: EntitiesCardEntityConfig; + @state() private _selectedEntityRow?: string; + public setConfig(config: EntitiesCardEntityConfig): void { if (!config || !config.entity) { throw new Error("Entity must be specified"); @@ -65,23 +65,14 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow { > ({ + value: option, + label: this.hass!.formatEntityState(stateObj, option), + }))} .disabled=${stateObj.state === UNAVAILABLE} - naturalMenuWidth - @action=${this._handleAction} - @click=${stopPropagation} - @closed=${stopPropagation} + @selected=${this._handleAction} > - ${stateObj.attributes.options - ? stateObj.attributes.options.map( - (option) => html` - - ${this.hass!.formatEntityState(stateObj, option)} - - ` - ) - : ""} `; @@ -98,10 +89,10 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow { } `; - private _handleAction(ev): void { + private _handleAction(ev: CustomEvent<{ value: string }>): void { const stateObj = this.hass!.states[this._config!.entity] as SelectEntity; - const option = ev.target.value; + const option = ev.detail.value; if ( option === stateObj.state || @@ -120,9 +111,7 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow { setTimeout(() => { const newStateObj = this.hass!.states[this._config!.entity]; if (newStateObj === stateObj) { - const select = this.shadowRoot?.querySelector("ha-select"); - const index = select?.options.indexOf(stateObj.state) ?? -1; - select?.select(index); + this._selectedEntityRow = stateObj.state; } }, 2000) ); diff --git a/src/panels/profile/ha-pick-dashboard-row.ts b/src/panels/profile/ha-pick-dashboard-row.ts index e3c41e439b..765f665732 100644 --- a/src/panels/profile/ha-pick-dashboard-row.ts +++ b/src/panels/profile/ha-pick-dashboard-row.ts @@ -1,9 +1,10 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; import "../../components/ha-divider"; +import "../../components/ha-dropdown-item"; import "../../components/ha-icon"; -import "../../components/ha-list-item"; import "../../components/ha-select"; import "../../components/ha-settings-row"; import "../../components/ha-spinner"; @@ -46,13 +47,15 @@ class HaPickDashboardRow extends LitElement { .label=${this.hass.localize( "ui.panel.profile.dashboard.dropdown_label" )} - .value=${value} + .value=${this._valueLabel(value)} @selected=${this._dashboardChanged} - naturalMenuWidth > - + ${this.hass.localize("ui.panel.profile.dashboard.system")} - + ${PANEL_DASHBOARDS.map((panel) => { const panelInfo = this.hass.panels[panel] as @@ -62,13 +65,16 @@ class HaPickDashboardRow extends LitElement { return nothing; } return html` - + ${getPanelTitle(this.hass, panelInfo)} - + `; })} ${this._dashboards.length @@ -82,16 +88,18 @@ class HaPickDashboardRow extends LitElement { return ""; } return html` - ${dashboard.title} - + `; })} ` @@ -109,8 +117,8 @@ class HaPickDashboardRow extends LitElement { this._dashboards = await fetchDashboards(this.hass); } - private _dashboardChanged(ev) { - const value = ev.target.value as string; + private _dashboardChanged(ev: CustomEvent<{ value: string }>): void { + const value = ev.detail.value; if (!value) { return; } @@ -124,6 +132,24 @@ class HaPickDashboardRow extends LitElement { }); } + private _valueLabel = memoizeOne((value: string) => { + if (value === USE_SYSTEM_VALUE) { + return this.hass.localize("ui.panel.profile.dashboard.system"); + } + if (value === "lovelace") { + return this.hass.localize("ui.panel.profile.dashboard.lovelace"); + } + const panelInfo = this.hass.panels[value] as PanelInfo | undefined; + if (panelInfo) { + return getPanelTitle(this.hass, panelInfo); + } + const dashboard = this._dashboards?.find((dash) => dash.url_path === value); + if (dashboard) { + return dashboard.title; + } + return value; + }); + static get styles(): CSSResultGroup { return [ css` @@ -134,6 +160,11 @@ class HaPickDashboardRow extends LitElement { height: 56px; width: 200px; } + + ha-select { + display: block; + width: 100%; + } `, ]; } diff --git a/src/panels/profile/ha-pick-date-format-row.ts b/src/panels/profile/ha-pick-date-format-row.ts index ead7a7cf0f..14744a3fea 100644 --- a/src/panels/profile/ha-pick-date-format-row.ts +++ b/src/panels/profile/ha-pick-date-format-row.ts @@ -1,10 +1,9 @@ import type { TemplateResult } from "lit"; -import { html, LitElement } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; import { formatDateNumeric } from "../../common/datetime/format_date"; import { fireEvent } from "../../common/dom/fire_event"; import "../../components/ha-card"; -import "../../components/ha-list-item"; import "../../components/ha-select"; import "../../components/ha-settings-row"; import { DateFormat } from "../../data/translation"; @@ -33,33 +32,36 @@ class DateFormatRow extends LitElement { .disabled=${this.hass.locale === undefined} .value=${this.hass.locale.date_format} @selected=${this._handleFormatSelection} - naturalMenuWidth - > - ${Object.values(DateFormat).map((format) => { - const formattedDate = formatDateNumeric( + .options=${Object.values(DateFormat).map((format) => ({ + value: format.toString(), + label: this.hass.localize( + `ui.panel.profile.date_format.formats.${format}` + ), + secondary: formatDateNumeric( date, { ...this.hass.locale, date_format: format, }, this.hass.config - ); - const value = this.hass.localize( - `ui.panel.profile.date_format.formats.${format}` - ); - return html` - ${value} - ${formattedDate} - `; - })} + ), + }))} + >
    `; } - private async _handleFormatSelection(ev) { - fireEvent(this, "hass-date-format-select", ev.target.value); + private async _handleFormatSelection(ev: CustomEvent<{ value: string }>) { + fireEvent(this, "hass-date-format-select", ev.detail.value as DateFormat); } + + static styles = css` + ha-select { + display: block; + width: 100%; + } + `; } declare global { diff --git a/src/panels/profile/ha-pick-first-weekday-row.ts b/src/panels/profile/ha-pick-first-weekday-row.ts index e793c93151..ef03d8d107 100644 --- a/src/panels/profile/ha-pick-first-weekday-row.ts +++ b/src/panels/profile/ha-pick-first-weekday-row.ts @@ -1,9 +1,8 @@ import type { TemplateResult } from "lit"; -import { html, LitElement } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; import { firstWeekday } from "../../common/datetime/first_weekday"; import { fireEvent } from "../../common/dom/fire_event"; -import "../../components/ha-list-item"; import "../../components/ha-select"; import "../../components/ha-settings-row"; import { FirstWeekday } from "../../data/translation"; @@ -31,43 +30,44 @@ class FirstWeekdayRow extends LitElement { .disabled=${this.hass.locale === undefined} .value=${this.hass.locale.first_weekday} @selected=${this._handleFormatSelection} - naturalMenuWidth - > - ${[ + .options=${[ FirstWeekday.language, FirstWeekday.monday, FirstWeekday.saturday, FirstWeekday.sunday, - ].map((day) => { - const value = this.hass.localize( + ].map((day) => ({ + value: day.toString(), + label: this.hass.localize( `ui.panel.profile.first_weekday.values.${day}` - ); - const twoLine = day === FirstWeekday.language; - return html` - - ${value} - ${twoLine - ? html` - ${this.hass.localize( - `ui.panel.profile.first_weekday.values.${firstWeekday( - this.hass.locale - )}` - )} - ` - : ""} - - `; - })} - + ), + secondary: + day === FirstWeekday.language + ? this.hass.localize( + `ui.panel.profile.first_weekday.values.${firstWeekday( + this.hass.locale + )}` + ) + : undefined, + }))} + >
    `; } - private async _handleFormatSelection(ev) { - fireEvent(this, "hass-first-weekday-select", ev.target.value); + private async _handleFormatSelection(ev: CustomEvent<{ value: string }>) { + fireEvent( + this, + "hass-first-weekday-select", + ev.detail.value as FirstWeekday + ); } + + static styles = css` + ha-select { + display: block; + width: 100%; + } + `; } declare global { diff --git a/src/panels/profile/ha-pick-number-format-row.ts b/src/panels/profile/ha-pick-number-format-row.ts index 356fd6122f..69d58d1909 100644 --- a/src/panels/profile/ha-pick-number-format-row.ts +++ b/src/panels/profile/ha-pick-number-format-row.ts @@ -1,10 +1,9 @@ import type { TemplateResult } from "lit"; -import { html, LitElement } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; import { fireEvent } from "../../common/dom/fire_event"; import { formatNumber } from "../../common/number/format_number"; import "../../components/ha-card"; -import "../../components/ha-list-item"; import "../../components/ha-select"; import "../../components/ha-settings-row"; import { NumberFormat } from "../../data/translation"; @@ -32,34 +31,42 @@ class NumberFormatRow extends LitElement { .disabled=${this.hass.locale === undefined} .value=${this.hass.locale.number_format} @selected=${this._handleFormatSelection} - naturalMenuWidth - > - ${Object.values(NumberFormat).map((format) => { - const formattedNumber = formatNumber(1234567.89, { - ...this.hass.locale, - number_format: format, - }); - const value = this.hass.localize( + .options=${Object.values(NumberFormat).map((format) => { + const label = this.hass.localize( `ui.panel.profile.number_format.formats.${format}` ); - const twoLine = value.slice(value.length - 2) !== "89"; // Display explicit number formats on one line - return html` - - ${value} - ${twoLine - ? html`${formattedNumber}` - : ""} - - `; + return { + value: format, + label, + secondary: + label.slice(label.length - 2) !== "89" + ? formatNumber(1234567.89, { + ...this.hass.locale, + number_format: format, + }) + : undefined, + }; })} + > `; } - private async _handleFormatSelection(ev) { - fireEvent(this, "hass-number-format-select", ev.target.value); + private async _handleFormatSelection(ev: CustomEvent<{ value: string }>) { + fireEvent( + this, + "hass-number-format-select", + ev.detail.value as NumberFormat + ); } + + static styles = css` + ha-select { + display: block; + width: 100%; + } + `; } declare global { diff --git a/src/panels/profile/ha-pick-theme-row.ts b/src/panels/profile/ha-pick-theme-row.ts index 0ed5a41f59..341eaba375 100644 --- a/src/panels/profile/ha-pick-theme-row.ts +++ b/src/panels/profile/ha-pick-theme-row.ts @@ -3,23 +3,22 @@ import { css, html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; import { normalizeLuminance } from "../../common/color/palette"; import { fireEvent } from "../../common/dom/fire_event"; -import "../../components/ha-formfield"; -import "../../components/ha-list-item"; -import "../../components/ha-radio"; import "../../components/ha-button"; +import "../../components/ha-formfield"; +import "../../components/ha-radio"; import type { HaRadio } from "../../components/ha-radio"; import "../../components/ha-select"; import "../../components/ha-settings-row"; import "../../components/ha-textfield"; -import { - DefaultAccentColor, - DefaultPrimaryColor, -} from "../../resources/theme/color/color.globals"; import { saveThemePreferences, subscribeThemePreferences, } from "../../data/theme"; import { SubscribeMixin } from "../../mixins/subscribe-mixin"; +import { + DefaultAccentColor, + DefaultPrimaryColor, +} from "../../resources/theme/color/color.globals"; import type { HomeAssistant, ThemeSettings } from "../../types"; import { documentationUrl } from "../../util/documentation-url"; import { clearSelectedThemeState } from "../../util/ha-pref-storage"; @@ -93,19 +92,18 @@ export class HaPickThemeRow extends SubscribeMixin(LitElement) { .disabled=${!hasThemes} .value=${this.hass.selectedTheme?.theme || USE_DEFAULT_THEME} @selected=${this._handleThemeSelection} - naturalMenuWidth + .options=${[ + { + value: USE_DEFAULT_THEME, + label: this.hass.localize("ui.panel.profile.themes.use_default"), + }, + { value: HOME_ASSISTANT_THEME, label: "Home Assistant" }, + ...this._themeNames.map((theme) => ({ + value: theme, + label: theme, + })), + ]} > - - ${this.hass.localize("ui.panel.profile.themes.use_default")} - - - Home Assistant - - ${this._themeNames.map( - (theme) => html` - ${theme} - ` - )} ${curTheme === HOME_ASSISTANT_THEME || @@ -261,8 +259,8 @@ export class HaPickThemeRow extends SubscribeMixin(LitElement) { fireEvent(this, "settheme", { dark }); } - private _handleThemeSelection(ev) { - const theme = ev.target.value; + private _handleThemeSelection(ev: CustomEvent<{ value: string }>) { + const theme = ev.detail.value; if (theme === this.hass.selectedTheme?.theme) { return; } @@ -334,6 +332,11 @@ export class HaPickThemeRow extends SubscribeMixin(LitElement) { flex-grow: 1; margin: 0 4px; } + + ha-select { + display: block; + width: 100%; + } `; } diff --git a/src/panels/profile/ha-pick-time-format-row.ts b/src/panels/profile/ha-pick-time-format-row.ts index 17644a7975..54a0755ea3 100644 --- a/src/panels/profile/ha-pick-time-format-row.ts +++ b/src/panels/profile/ha-pick-time-format-row.ts @@ -1,10 +1,9 @@ import type { TemplateResult } from "lit"; -import { html, LitElement } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; import { formatTime } from "../../common/datetime/format_time"; import { fireEvent } from "../../common/dom/fire_event"; import "../../components/ha-card"; -import "../../components/ha-list-item"; import "../../components/ha-select"; import "../../components/ha-settings-row"; import { TimeFormat } from "../../data/translation"; @@ -33,33 +32,36 @@ class TimeFormatRow extends LitElement { .disabled=${this.hass.locale === undefined} .value=${this.hass.locale.time_format} @selected=${this._handleFormatSelection} - naturalMenuWidth - > - ${Object.values(TimeFormat).map((format) => { - const formattedTime = formatTime( + .options=${Object.values(TimeFormat).map((format) => ({ + value: format.toString(), + label: this.hass.localize( + `ui.panel.profile.time_format.formats.${format}` + ), + secondary: formatTime( date, { ...this.hass.locale, time_format: format, }, this.hass.config - ); - const value = this.hass.localize( - `ui.panel.profile.time_format.formats.${format}` - ); - return html` - ${value} - ${formattedTime} - `; - })} + ), + }))} + > `; } - private async _handleFormatSelection(ev) { - fireEvent(this, "hass-time-format-select", ev.target.value); + private async _handleFormatSelection(ev: CustomEvent<{ value: string }>) { + fireEvent(this, "hass-time-format-select", ev.detail.value as TimeFormat); } + + static styles = css` + ha-select { + display: block; + width: 100%; + } + `; } declare global { diff --git a/src/panels/profile/ha-pick-time-zone-row.ts b/src/panels/profile/ha-pick-time-zone-row.ts index a237eb6ec9..24fa7bfd12 100644 --- a/src/panels/profile/ha-pick-time-zone-row.ts +++ b/src/panels/profile/ha-pick-time-zone-row.ts @@ -1,11 +1,10 @@ import type { TemplateResult } from "lit"; -import { html, LitElement } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; import { formatDateTimeNumeric } from "../../common/datetime/format_date_time"; import { resolveTimeZone } from "../../common/datetime/resolve-time-zone"; import { fireEvent } from "../../common/dom/fire_event"; import "../../components/ha-card"; -import "../../components/ha-list-item"; import "../../components/ha-select"; import "../../components/ha-settings-row"; import { TimeZone } from "../../data/translation"; @@ -34,40 +33,42 @@ class TimeZoneRow extends LitElement { .disabled=${this.hass.locale === undefined} .value=${this.hass.locale.time_zone} @selected=${this._handleFormatSelection} - naturalMenuWidth - > - ${Object.values(TimeZone).map((format) => { - const formattedTime = formatDateTimeNumeric( + .options=${Object.values(TimeZone).map((format) => ({ + value: format.toString(), + label: this.hass.localize( + `ui.panel.profile.time_zone.options.${format}`, + { + timezone: resolveTimeZone( + format, + this.hass.config.time_zone + ).replace("_", " "), + } + ), + secondary: formatDateTimeNumeric( date, { ...this.hass.locale, time_zone: format, }, this.hass.config - ); - return html` - ${this.hass.localize( - `ui.panel.profile.time_zone.options.${format}`, - { - timezone: resolveTimeZone( - format, - this.hass.config.time_zone - ).replace("_", " "), - } - )} - ${formattedTime} - `; - })} + ), + }))} + > `; } - private async _handleFormatSelection(ev) { - fireEvent(this, "hass-time-zone-select", ev.target.value); + private async _handleFormatSelection(ev: CustomEvent<{ value: string }>) { + fireEvent(this, "hass-time-zone-select", ev.detail.value as TimeZone); } + + static styles = css` + ha-select { + display: block; + width: 100%; + } + `; } declare global { diff --git a/src/state-summary/state-card-input_select.ts b/src/state-summary/state-card-input_select.ts index 2a52cd9eb2..87a626e0b7 100644 --- a/src/state-summary/state-card-input_select.ts +++ b/src/state-summary/state-card-input_select.ts @@ -2,9 +2,9 @@ import type { TemplateResult } from "lit"; import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; import { computeStateName } from "../common/entity/compute_state_name"; -import type { HaDropdownSelectEvent } from "../components/ha-dropdown"; import "../components/entity/state-badge"; import "../components/ha-control-select-menu"; +import type { HaDropdownSelectEvent } from "../components/ha-dropdown"; import { UNAVAILABLE } from "../data/entity/entity"; import type { InputSelectEntity } from "../data/input_select"; import { setInputSelectOption } from "../data/input_select"; diff --git a/src/state-summary/state-card-select.ts b/src/state-summary/state-card-select.ts index 083ac7966e..0956f280f3 100644 --- a/src/state-summary/state-card-select.ts +++ b/src/state-summary/state-card-select.ts @@ -2,9 +2,9 @@ import type { TemplateResult } from "lit"; import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; import { computeStateName } from "../common/entity/compute_state_name"; -import type { HaDropdownSelectEvent } from "../components/ha-dropdown"; import "../components/entity/state-badge"; import "../components/ha-control-select-menu"; +import type { HaDropdownSelectEvent } from "../components/ha-dropdown"; import { UNAVAILABLE } from "../data/entity/entity"; import type { SelectEntity } from "../data/select"; import { setSelectOption } from "../data/select"; diff --git a/src/state/translations-mixin.ts b/src/state/translations-mixin.ts index 2b587acadb..4aca311817 100644 --- a/src/state/translations-mixin.ts +++ b/src/state/translations-mixin.ts @@ -37,21 +37,11 @@ declare global { "hass-language-select": { language: string; }; - "hass-number-format-select": { - number_format: NumberFormat; - }; - "hass-time-format-select": { - time_format: TimeFormat; - }; - "hass-date-format-select": { - date_format: DateFormat; - }; - "hass-time-zone-select": { - time_zone: TimeZone; - }; - "hass-first-weekday-select": { - first_weekday: FirstWeekday; - }; + "hass-number-format-select": NumberFormat; + "hass-time-format-select": TimeFormat; + "hass-date-format-select": DateFormat; + "hass-time-zone-select": TimeZone; + "hass-first-weekday-select": FirstWeekday; "translations-updated": undefined; } } diff --git a/yarn.lock b/yarn.lock index 01eaef5c47..a64192c225 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2903,7 +2903,7 @@ __metadata: languageName: node linkType: hard -"@material/mwc-menu@npm:0.27.0, @material/mwc-menu@npm:^0.27.0": +"@material/mwc-menu@npm:^0.27.0": version: 0.27.0 resolution: "@material/mwc-menu@npm:0.27.0" dependencies: @@ -9103,7 +9103,6 @@ __metadata: "@material/mwc-icon-button": "npm:0.27.0" "@material/mwc-linear-progress": "npm:0.27.0" "@material/mwc-list": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch" - "@material/mwc-menu": "npm:0.27.0" "@material/mwc-radio": "npm:0.27.0" "@material/mwc-select": "npm:0.27.0" "@material/mwc-snackbar": "npm:0.27.0"