Render target picker combo-box in overlaying menu surface (#14970)

This commit is contained in:
Bram Kragten 2023-02-20 17:32:52 +01:00 committed by GitHub
parent cabbbcf9f3
commit 5f9f51f92d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 146 additions and 120 deletions

View File

@ -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;

View File

@ -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}

View File

@ -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);
});
}

View File

@ -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`
<div class="horizontal-container">
${this._renderChips()} ${this._renderPicker()}
</div>
${this._renderItems()}
`
: html`
<div>
${this._renderItems()} ${this._renderPicker()}
${this._renderChips()}
</div>
`}
`;
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`
<div class="mdc-chip-set">
<div class="mdc-chip-set add-container">
<div
class="mdc-chip area_id add"
.type=${"area_id"}
@ -193,6 +189,7 @@ export class HaTargetPicker extends LitElement {
</span>
</span>
</div>
${this._renderPicker()}
</div>
${this.helper
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`
@ -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 {
</span>
${type === "entity_id"
? ""
: html` <span role="gridcell">
: html`<span role="gridcell">
<ha-icon-button
class="expand-btn mdc-chip__icon mdc-chip__icon--trailing"
tabindex="-1"
@ -282,61 +276,72 @@ export class HaTargetPicker extends LitElement {
}
private _renderPicker() {
switch (this._addMode) {
case "area_id":
return html`
<ha-area-picker
.hass=${this.hass}
id="input"
.type=${"area_id"}
.label=${this.hass.localize(
"ui.components.target-picker.add_area_id"
)}
no-add
.deviceFilter=${this.deviceFilter}
.entityFilter=${this.entityFilter}
.includeDeviceClasses=${this.includeDeviceClasses}
.includeDomains=${this.includeDomains}
.excludeAreas=${ensureArray(this.value?.area_id)}
@value-changed=${this._targetPicked}
></ha-area-picker>
`;
case "device_id":
return html`
<ha-device-picker
.hass=${this.hass}
id="input"
.type=${"device_id"}
.label=${this.hass.localize(
"ui.components.target-picker.add_device_id"
)}
.deviceFilter=${this.deviceFilter}
.entityFilter=${this.entityFilter}
.includeDeviceClasses=${this.includeDeviceClasses}
.includeDomains=${this.includeDomains}
.excludeDevices=${ensureArray(this.value?.device_id)}
@value-changed=${this._targetPicked}
></ha-device-picker>
`;
case "entity_id":
return html`
<ha-entity-picker
.hass=${this.hass}
id="input"
.type=${"entity_id"}
.label=${this.hass.localize(
"ui.components.target-picker.add_entity_id"
)}
.entityFilter=${this.entityFilter}
.includeDeviceClasses=${this.includeDeviceClasses}
.includeDomains=${this.includeDomains}
.excludeEntities=${ensureArray(this.value?.entity_id)}
@value-changed=${this._targetPicked}
allow-custom-entity
></ha-entity-picker>
`;
if (!this._addMode) {
return html``;
}
return html``;
return html`<mwc-menu-surface
open
.anchor=${this._addContainer}
.corner=${"BOTTOM_START"}
@closed=${this._onClosed}
@opened=${this._onOpened}
@opened-changed=${this._openedChanged}
@input=${stopPropagation}
>${this._addMode === "area_id"
? html`
<ha-area-picker
.hass=${this.hass}
id="input"
.type=${"area_id"}
.label=${this.hass.localize(
"ui.components.target-picker.add_area_id"
)}
no-add
.deviceFilter=${this.deviceFilter}
.entityFilter=${this.entityFilter}
.includeDeviceClasses=${this.includeDeviceClasses}
.includeDomains=${this.includeDomains}
.excludeAreas=${ensureArray(this.value?.area_id)}
@value-changed=${this._targetPicked}
@click=${this._preventDefault}
></ha-area-picker>
`
: this._addMode === "device_id"
? html`
<ha-device-picker
.hass=${this.hass}
id="input"
.type=${"device_id"}
.label=${this.hass.localize(
"ui.components.target-picker.add_device_id"
)}
.deviceFilter=${this.deviceFilter}
.entityFilter=${this.entityFilter}
.includeDeviceClasses=${this.includeDeviceClasses}
.includeDomains=${this.includeDomains}
.excludeDevices=${ensureArray(this.value?.device_id)}
@value-changed=${this._targetPicked}
@click=${this._preventDefault}
></ha-device-picker>
`
: html`
<ha-entity-picker
.hass=${this.hass}
id="input"
.type=${"entity_id"}
.label=${this.hass.localize(
"ui.components.target-picker.add_entity_id"
)}
.entityFilter=${this.entityFilter}
.includeDeviceClasses=${this.includeDeviceClasses}
.includeDomains=${this.includeDomains}
.excludeEntities=${ensureArray(this.value?.entity_id)}
@value-changed=${this._targetPicked}
@click=${this._preventDefault}
allow-custom-entity
></ha-entity-picker>
`}</mwc-menu-surface
>`;
}
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%;
}
`;
}
}

View File

@ -156,7 +156,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
</app-header>
<div class="flex content">
<div class="filters flex layout horizontal narrow-wrap">
<div class="filters">
<ha-date-range-picker
.hass=${this.hass}
?disabled=${this._isLoading}
@ -169,7 +169,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
.hass=${this.hass}
.value=${this._targetPickerValue}
.disabled=${this._isLoading}
horizontal
addOnTop
@value-changed=${this._targetsChanged}
></ha-target-picker>
</div>
@ -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;

View File

@ -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 {