From 5f9f51f92d8667d25694fd343dddd9ca102c0099 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 20 Feb 2023 17:32:52 +0100 Subject: [PATCH] Render target picker combo-box in overlaying menu surface (#14970) --- src/components/device/ha-device-picker.ts | 2 +- src/components/device/ha-devices-picker.ts | 10 +- src/components/ha-area-picker.ts | 3 + src/components/ha-target-picker.ts | 202 ++++++++++++--------- src/panels/history/ha-panel-history.ts | 47 ++--- src/panels/logbook/ha-panel-logbook.ts | 2 + 6 files changed, 146 insertions(+), 120 deletions(-) diff --git a/src/components/device/ha-device-picker.ts b/src/components/device/ha-device-picker.ts index 715a42c1db..8b9e477d6a 100644 --- a/src/components/device/ha-device-picker.ts +++ b/src/components/device/ha-device-picker.ts @@ -214,7 +214,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { if (!devEntities || !devEntities.length) { return false; } - return deviceEntityLookup[device.id].some((entity) => { + return devEntities.some((entity) => { const stateObj = this.hass.states[entity.entity_id]; if (!stateObj) { return false; diff --git a/src/components/device/ha-devices-picker.ts b/src/components/device/ha-devices-picker.ts index 1f1340ef04..327af4c8ac 100644 --- a/src/components/device/ha-devices-picker.ts +++ b/src/components/device/ha-devices-picker.ts @@ -4,7 +4,10 @@ import { fireEvent } from "../../common/dom/fire_event"; import { PolymerChangedEvent } from "../../polymer-types"; import { HomeAssistant } from "../../types"; import "./ha-device-picker"; -import type { HaDevicePickerDeviceFilterFunc } from "./ha-device-picker"; +import type { + HaDevicePickerDeviceFilterFunc, + HaDevicePickerEntityFilterFunc, +} from "./ha-device-picker"; @customElement("ha-devices-picker") class HaDevicesPicker extends LitElement { @@ -44,6 +47,8 @@ class HaDevicesPicker extends LitElement { @property() public deviceFilter?: HaDevicePickerDeviceFilterFunc; + @property() public entityFilter?: HaDevicePickerEntityFilterFunc; + protected render(): TemplateResult { if (!this.hass) { return html``; @@ -59,6 +64,7 @@ class HaDevicesPicker extends LitElement { .curValue=${entityId} .hass=${this.hass} .deviceFilter=${this.deviceFilter} + .entityFilter=${this.entityFilter} .includeDomains=${this.includeDomains} .excludeDomains=${this.excludeDomains} .includeDeviceClasses=${this.includeDeviceClasses} @@ -76,8 +82,10 @@ class HaDevicesPicker extends LitElement { .hass=${this.hass} .helper=${this.helper} .deviceFilter=${this.deviceFilter} + .entityFilter=${this.entityFilter} .includeDomains=${this.includeDomains} .excludeDomains=${this.excludeDomains} + .excludeDevices=${currentDevices} .includeDeviceClasses=${this.includeDeviceClasses} .label=${this.pickDeviceLabel} .disabled=${this.disabled} diff --git a/src/components/ha-area-picker.ts b/src/components/ha-area-picker.ts index 0f47ed05b9..6f016cc1f7 100644 --- a/src/components/ha-area-picker.ts +++ b/src/components/ha-area-picker.ts @@ -233,6 +233,9 @@ export class HaAreaPicker extends LitElement { }); inputEntities = inputEntities!.filter((entity) => { const stateObj = this.hass.states[entity.entity_id]; + if (!stateObj) { + return false; + } return entityFilter!(stateObj); }); } diff --git a/src/components/ha-target-picker.ts b/src/components/ha-target-picker.ts index 599720075a..02eb8f1456 100644 --- a/src/components/ha-target-picker.ts +++ b/src/components/ha-target-picker.ts @@ -13,6 +13,7 @@ import { HassEntity, HassServiceTarget } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, unsafeCSS } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; +import { ComboBoxLightOpenedChangedEvent } from "@vaadin/combo-box/vaadin-combo-box-light"; import { ensureArray } from "../common/array/ensure-array"; import { fireEvent } from "../common/dom/fire_event"; import { computeDomain } from "../common/entity/compute_domain"; @@ -31,6 +32,8 @@ import "./ha-area-picker"; import "./ha-icon-button"; import "./ha-input-helper-text"; import "./ha-svg-icon"; +import { stopPropagation } from "../common/dom/stop_propagation"; +import "@material/mwc-menu/mwc-menu-surface"; @customElement("ha-target-picker") export class HaTargetPicker extends LitElement { @@ -64,28 +67,21 @@ export class HaTargetPicker extends LitElement { @property({ type: Boolean, reflect: true }) public disabled = false; - @property({ type: Boolean }) public horizontal = false; + @property({ type: Boolean }) public addOnTop = false; @state() private _addMode?: "area_id" | "entity_id" | "device_id"; @query("#input") private _inputElement?; + @query(".add-container", true) private _addContainer?: HTMLDivElement; + + private _opened = false; + protected render() { - return html` - ${this.horizontal - ? html` -
- ${this._renderChips()} ${this._renderPicker()} -
- ${this._renderItems()} - ` - : html` -
- ${this._renderItems()} ${this._renderPicker()} - ${this._renderChips()} -
- `} - `; + if (this.addOnTop) { + return html` ${this._renderChips()} ${this._renderItems()} `; + } + return html` ${this._renderItems()} ${this._renderChips()} `; } private _renderItems() { @@ -132,7 +128,7 @@ export class HaTargetPicker extends LitElement { private _renderChips() { return html` -
+
+ ${this._renderPicker()}
${this.helper ? html`${this.helper}` @@ -200,11 +197,8 @@ export class HaTargetPicker extends LitElement { `; } - private async _showPicker(ev) { + private _showPicker(ev) { this._addMode = ev.currentTarget.type; - await this.updateComplete; - await this._inputElement?.focus(); - await this._inputElement?.open(); } private _renderChip( @@ -239,7 +233,7 @@ export class HaTargetPicker extends LitElement { ${type === "entity_id" ? "" - : html` + : html` - `; - case "device_id": - return html` - - `; - case "entity_id": - return html` - - `; + if (!this._addMode) { + return html``; } - return html``; + return html`${this._addMode === "area_id" + ? html` + + ` + : this._addMode === "device_id" + ? html` + + ` + : html` + + `}`; } private _targetPicked(ev) { @@ -347,7 +352,6 @@ export class HaTargetPicker extends LitElement { const value = ev.detail.value; const target = ev.currentTarget; target.value = ""; - this._addMode = undefined; if ( this.value && this.value[target.type] && @@ -454,6 +458,31 @@ export class HaTargetPicker extends LitElement { return undefined; } + private _onClosed(ev) { + ev.stopPropagation(); + ev.target.open = true; + } + + private async _onOpened() { + if (!this._addMode) { + return; + } + await this._inputElement?.focus(); + await this._inputElement?.open(); + this._opened = true; + } + + private _openedChanged(ev: ComboBoxLightOpenedChangedEvent) { + if (this._opened && !ev.detail.value) { + this._opened = false; + this._addMode = undefined; + } + } + + private _preventDefault(ev: Event) { + ev.preventDefault(); + } + private _deviceMeetsFilter(device: DeviceRegistryEntry): boolean { const devEntities = Object.values(this.hass.entities).filter( (entity) => entity.device_id === device.id @@ -555,12 +584,6 @@ export class HaTargetPicker extends LitElement { static get styles(): CSSResultGroup { return css` ${unsafeCSS(chipStyles)} - .horizontal-container { - display: flex; - flex-wrap: wrap; - min-height: 56px; - align-items: center; - } .mdc-chip { color: var(--primary-text-color); } @@ -573,6 +596,10 @@ export class HaTargetPicker extends LitElement { .mdc-chip.add { color: rgba(0, 0, 0, 0.87); } + .add-container { + position: relative; + display: inline-flex; + } .mdc-chip:not(.add) { cursor: default; } @@ -644,6 +671,15 @@ export class HaTargetPicker extends LitElement { opacity: var(--light-disabled-opacity); pointer-events: none; } + mwc-menu-surface { + --mdc-menu-min-width: 100%; + } + ha-entity-picker, + ha-device-picker, + ha-area-picker { + display: block; + width: 100%; + } `; } } diff --git a/src/panels/history/ha-panel-history.ts b/src/panels/history/ha-panel-history.ts index b3be7664b4..cdaf307565 100644 --- a/src/panels/history/ha-panel-history.ts +++ b/src/panels/history/ha-panel-history.ts @@ -156,7 +156,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
-
+
@@ -510,18 +510,6 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { height: 100%; } - :host([narrow]) .narrow-wrap { - flex-wrap: wrap; - } - - .horizontal { - align-items: center; - } - - :host(:not([narrow])) .selector-padding { - padding-left: 32px; - } - .progress-wrapper { position: relative; } @@ -529,11 +517,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { .filters { display: flex; align-items: flex-start; - padding: 8px 16px 0; - } - - :host([narrow]) .filters { - flex-wrap: wrap; + margin-top: 16px; } ha-date-range-picker { @@ -544,11 +528,15 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { direction: var(--direction); } - :host([narrow]) ha-date-range-picker { - margin-right: 0; - margin-inline-end: 0; - margin-inline-start: initial; - direction: var(--direction); + @media all and (max-width: 1025px) { + .filters { + flex-direction: column; + } + ha-date-range-picker { + margin-right: 0; + margin-inline-end: 0; + width: 100%; + } } ha-circular-progress { @@ -558,17 +546,6 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { transform: translate(-50%, -50%); } - ha-entity-picker { - display: inline-block; - flex-grow: 1; - max-width: 400px; - } - - :host([narrow]) ha-entity-picker { - max-width: none; - width: 100%; - } - .start-search { padding-top: 16px; text-align: center; diff --git a/src/panels/logbook/ha-panel-logbook.ts b/src/panels/logbook/ha-panel-logbook.ts index 8f53ce7f56..f3983a2c4b 100644 --- a/src/panels/logbook/ha-panel-logbook.ts +++ b/src/panels/logbook/ha-panel-logbook.ts @@ -249,6 +249,7 @@ export class HaPanelLogbook extends LitElement { margin-inline-start: initial; max-width: 100%; direction: var(--direction); + margin-bottom: -5px; } :host([narrow]) ha-date-range-picker { @@ -256,6 +257,7 @@ export class HaPanelLogbook extends LitElement { margin-inline-end: 0; margin-inline-start: initial; direction: var(--direction); + margin-bottom: 8px; } .filters {