Add message when no matches for selectors, clear config when deleted (#7906)

This commit is contained in:
Bram Kragten 2020-12-04 20:49:24 +01:00 committed by GitHub
parent e014c7aff6
commit f1abb60e4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 184 additions and 79 deletions

View File

@ -1,4 +1,5 @@
import "../ha-icon-button"; import "../ha-svg-icon";
import "@material/mwc-icon-button/mwc-icon-button";
import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
@ -37,6 +38,7 @@ import {
import { SubscribeMixin } from "../../mixins/subscribe-mixin"; import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { PolymerChangedEvent } from "../../polymer-types"; import { PolymerChangedEvent } from "../../polymer-types";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { mdiClose, mdiMenuUp, mdiMenuDown } from "@mdi/js";
interface Device { interface Device {
name: string; name: string;
@ -115,17 +117,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
@query("vaadin-combo-box-light", true) private _comboBox!: HTMLElement; @query("vaadin-combo-box-light", true) private _comboBox!: HTMLElement;
public open() { private _init = false;
this.updateComplete.then(() => {
(this.shadowRoot?.querySelector("vaadin-combo-box-light") as any)?.open();
});
}
public focus() {
this.updateComplete.then(() => {
this.shadowRoot?.querySelector("paper-input")?.focus();
});
}
private _getDevices = memoizeOne( private _getDevices = memoizeOne(
( (
@ -138,7 +130,13 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
deviceFilter: this["deviceFilter"] deviceFilter: this["deviceFilter"]
): Device[] => { ): Device[] => {
if (!devices.length) { if (!devices.length) {
return []; return [
{
id: "",
area: "",
name: this.hass.localize("ui.components.device-picker.no_devices"),
},
];
} }
const deviceEntityLookup: DeviceEntityLookup = {}; const deviceEntityLookup: DeviceEntityLookup = {};
@ -229,6 +227,15 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
: this.hass.localize("ui.components.device-picker.no_area"), : this.hass.localize("ui.components.device-picker.no_area"),
}; };
}); });
if (!outputDevices.length) {
return [
{
id: "",
area: "",
name: this.hass.localize("ui.components.device-picker.no_match"),
},
];
}
if (outputDevices.length === 1) { if (outputDevices.length === 1) {
return outputDevices; return outputDevices;
} }
@ -236,6 +243,18 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
} }
); );
public open() {
this.updateComplete.then(() => {
(this.shadowRoot?.querySelector("vaadin-combo-box-light") as any)?.open();
});
}
public focus() {
this.updateComplete.then(() => {
this.shadowRoot?.querySelector("paper-input")?.focus();
});
}
public hassSubscribe(): UnsubscribeFunc[] { public hassSubscribe(): UnsubscribeFunc[] {
return [ return [
subscribeDeviceRegistry(this.hass.connection!, (devices) => { subscribeDeviceRegistry(this.hass.connection!, (devices) => {
@ -251,7 +270,11 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
} }
protected updated(changedProps: PropertyValues) { protected updated(changedProps: PropertyValues) {
if (changedProps.has("_opened") && this._opened) { if (
(!this._init && this.devices && this.areas && this.entities) ||
(changedProps.has("_opened") && this._opened)
) {
this._init = true;
(this._comboBox as any).items = this._getDevices( (this._comboBox as any).items = this._getDevices(
this.devices!, this.devices!,
this.areas!, this.areas!,
@ -290,30 +313,30 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
> >
${this.value ${this.value
? html` ? html`
<ha-icon-button <mwc-icon-button
aria-label=${this.hass.localize( .label=${this.hass.localize(
"ui.components.device-picker.clear" "ui.components.device-picker.clear"
)} )}
slot="suffix" slot="suffix"
class="clear-button" class="clear-button"
icon="hass:close"
@click=${this._clearValue} @click=${this._clearValue}
no-ripple
> >
Clear <ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</ha-icon-button> </mwc-icon-button>
` `
: ""} : ""}
<ha-icon-button
aria-label=${this.hass.localize( <mwc-icon-button
.label=${this.hass.localize(
"ui.components.device-picker.show_devices" "ui.components.device-picker.show_devices"
)} )}
slot="suffix" slot="suffix"
class="toggle-button" class="toggle-button"
.icon=${this._opened ? "hass:menu-up" : "hass:menu-down"}
> >
Toggle <ha-svg-icon
</ha-icon-button> .path=${this._opened ? mdiMenuUp : mdiMenuDown}
></ha-svg-icon>
</mwc-icon-button>
</paper-input> </paper-input>
</vaadin-combo-box-light> </vaadin-combo-box-light>
`; `;
@ -350,7 +373,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
static get styles(): CSSResult { static get styles(): CSSResult {
return css` return css`
paper-input > ha-icon-button { paper-input > mwc-icon-button {
--mdc-icon-button-size: 24px; --mdc-icon-button-size: 24px;
padding: 2px; padding: 2px;
color: var(--secondary-text-color); color: var(--secondary-text-color);

View File

@ -165,6 +165,24 @@ export class HaEntityPicker extends LitElement {
); );
} }
if (!states.length) {
return [
{
entity_id: "",
state: "",
last_changed: "",
last_updated: "",
context: { id: "", user_id: null },
attributes: {
friendly_name: this.hass!.localize(
"ui.components.entity.entity-picker.no_match"
),
icon: "mdi:magnify",
},
},
];
}
return states; return states;
} }
); );
@ -215,7 +233,6 @@ export class HaEntityPicker extends LitElement {
.label=${this.label === undefined .label=${this.label === undefined
? this.hass.localize("ui.components.entity.entity-picker.entity") ? this.hass.localize("ui.components.entity.entity-picker.entity")
: this.label} : this.label}
.value=${this._value}
.disabled=${this.disabled} .disabled=${this.disabled}
class="input" class="input"
autocapitalize="none" autocapitalize="none"

View File

@ -1,4 +1,5 @@
import "./ha-icon-button"; import "./ha-svg-icon";
import "@material/mwc-icon-button/mwc-icon-button";
import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
@ -14,6 +15,8 @@ import {
property, property,
internalProperty, internalProperty,
TemplateResult, TemplateResult,
PropertyValues,
query,
} from "lit-element"; } from "lit-element";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { import {
@ -40,6 +43,7 @@ import {
} from "../data/entity_registry"; } from "../data/entity_registry";
import { computeDomain } from "../common/entity/compute_domain"; import { computeDomain } from "../common/entity/compute_domain";
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker"; import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js";
const rowRenderer = ( const rowRenderer = (
root: HTMLElement, root: HTMLElement,
@ -121,6 +125,10 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
@internalProperty() private _opened?: boolean; @internalProperty() private _opened?: boolean;
@query("vaadin-combo-box-light", true) private _comboBox!: HTMLElement;
private _init = false;
public hassSubscribe(): UnsubscribeFunc[] { public hassSubscribe(): UnsubscribeFunc[] {
return [ return [
subscribeAreaRegistry(this.hass.connection!, (areas) => { subscribeAreaRegistry(this.hass.connection!, (areas) => {
@ -159,6 +167,15 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
entityFilter: this["entityFilter"], entityFilter: this["entityFilter"],
noAdd: this["noAdd"] noAdd: this["noAdd"]
): AreaRegistryEntry[] => { ): AreaRegistryEntry[] => {
if (!areas.length) {
return [
{
area_id: "",
name: this.hass.localize("ui.components.area-picker.no_areas"),
},
];
}
const deviceEntityLookup: DeviceEntityLookup = {}; const deviceEntityLookup: DeviceEntityLookup = {};
let inputDevices: DeviceRegistryEntry[] | undefined; let inputDevices: DeviceRegistryEntry[] | undefined;
let inputEntities: EntityRegistryEntry[] | undefined; let inputEntities: EntityRegistryEntry[] | undefined;
@ -243,7 +260,9 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
} }
if (entityFilter) { if (entityFilter) {
entities = entities.filter((entity) => entityFilter!(entity)); inputEntities = inputEntities!.filter((entity) =>
entityFilter!(entity)
);
} }
let outputAreas = areas; let outputAreas = areas;
@ -268,6 +287,15 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
outputAreas = areas.filter((area) => areaIds!.includes(area.area_id)); outputAreas = areas.filter((area) => areaIds!.includes(area.area_id));
} }
if (!outputAreas.length) {
outputAreas = [
{
area_id: "",
name: this.hass.localize("ui.components.area-picker.no_match"),
},
];
}
return noAdd return noAdd
? outputAreas ? outputAreas
: [ : [
@ -280,27 +308,35 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
} }
); );
protected updated(changedProps: PropertyValues) {
if (
(!this._init && this._devices && this._areas && this._entities) ||
(changedProps.has("_opened") && this._opened)
) {
this._init = true;
(this._comboBox as any).items = this._getAreas(
this._areas!,
this._devices!,
this._entities!,
this.includeDomains,
this.excludeDomains,
this.includeDeviceClasses,
this.deviceFilter,
this.entityFilter,
this.noAdd
);
}
}
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this._devices || !this._areas || !this._entities) { if (!this._devices || !this._areas || !this._entities) {
return html``; return html``;
} }
const areas = this._getAreas(
this._areas,
this._devices,
this._entities,
this.includeDomains,
this.excludeDomains,
this.includeDeviceClasses,
this.deviceFilter,
this.entityFilter,
this.noAdd
);
return html` return html`
<vaadin-combo-box-light <vaadin-combo-box-light
item-value-path="area_id" item-value-path="area_id"
item-id-path="area_id" item-id-path="area_id"
item-label-path="name" item-label-path="name"
.items=${areas}
.value=${this._value} .value=${this._value}
.renderer=${rowRenderer} .renderer=${rowRenderer}
@opened-changed=${this._openedChanged} @opened-changed=${this._openedChanged}
@ -321,34 +357,28 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
> >
${this.value ${this.value
? html` ? html`
<ha-icon-button <mwc-icon-button
aria-label=${this.hass.localize( .label=${this.hass.localize(
"ui.components.area-picker.clear" "ui.components.area-picker.clear"
)} )}
slot="suffix" slot="suffix"
class="clear-button" class="clear-button"
icon="hass:close"
@click=${this._clearValue} @click=${this._clearValue}
no-ripple
> >
${this.hass.localize("ui.components.area-picker.clear")} <ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</ha-icon-button> </mwc-icon-button>
`
: ""}
${areas.length > 0
? html`
<ha-icon-button
aria-label=${this.hass.localize(
"ui.components.area-picker.show_areas"
)}
slot="suffix"
class="toggle-button"
.icon=${this._opened ? "hass:menu-up" : "hass:menu-down"}
>
${this.hass.localize("ui.components.area-picker.toggle")}
</ha-icon-button>
` `
: ""} : ""}
<mwc-icon-button
.label=${this.hass.localize("ui.components.area-picker.toggle")}
slot="suffix"
class="toggle-button"
>
<ha-svg-icon
.path=${this._opened ? mdiMenuUp : mdiMenuDown}
></ha-svg-icon>
</mwc-icon-button>
</paper-input> </paper-input>
</vaadin-combo-box-light> </vaadin-combo-box-light>
`; `;
@ -424,7 +454,7 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
static get styles(): CSSResult { static get styles(): CSSResult {
return css` return css`
paper-input > ha-icon-button { paper-input > mwc-icon-button {
--mdc-icon-button-size: 24px; --mdc-icon-button-size: 24px;
padding: 2px; padding: 2px;
color: var(--secondary-text-color); color: var(--secondary-text-color);

View File

@ -77,7 +77,8 @@ export class HaAreaSelector extends LitElement {
} }
if (this.selector.area.device?.integration) { if (this.selector.area.device?.integration) {
if ( if (
!this._configEntries?.some((entry) => this._configEntries &&
!this._configEntries.some((entry) =>
device.config_entries.includes(entry.entry_id) device.config_entries.includes(entry.entry_id)
) )
) { ) {

View File

@ -63,7 +63,8 @@ export class HaDeviceSelector extends LitElement {
} }
if (this.selector.device.integration) { if (this.selector.device.integration) {
if ( if (
!this._configEntries?.some((entry) => this._configEntries &&
!this._configEntries.some((entry) =>
device.config_entries.includes(entry.entry_id) device.config_entries.includes(entry.entry_id)
) )
) { ) {

View File

@ -435,10 +435,19 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
type: string, type: string,
id: string id: string
): this["value"] { ): this["value"] {
return { const newVal = ensureArray(value![type])!.filter((val) => val !== id);
...value, if (newVal.length) {
[type]: ensureArray(value![type])!.filter((val) => val !== id), return {
}; ...value,
[type]: newVal,
};
}
const val = { ...value }!;
delete val[type];
if (Object.keys(val).length) {
return val;
}
return undefined;
} }
private _deviceMeetsFilter(device: DeviceRegistryEntry): boolean { private _deviceMeetsFilter(device: DeviceRegistryEntry): boolean {

View File

@ -18,9 +18,6 @@ import "@polymer/paper-input/paper-textarea";
import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light"; import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
import "../../../components/entity/ha-entity-toggle"; import "../../../components/entity/ha-entity-toggle";
import "@material/mwc-button/mwc-button"; import "@material/mwc-button/mwc-button";
import "./trigger/ha-automation-trigger";
import "./condition/ha-automation-condition";
import "./action/ha-automation-action";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import { HassEntity } from "home-assistant-js-websocket"; import { HassEntity } from "home-assistant-js-websocket";
@ -151,7 +148,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
There is an error in this Blueprint: ${blueprint.error} There is an error in this Blueprint: ${blueprint.error}
</p>` </p>`
: html`${blueprint?.metadata.description : html`${blueprint?.metadata.description
? html`<p class="card-content"> ? html`<p class="card-content pre-line">
${blueprint.metadata.description} ${blueprint.metadata.description}
</p>` </p>`
: ""} : ""}
@ -227,12 +224,18 @@ export class HaBlueprintAutomationEditor extends LitElement {
) { ) {
return; return;
} }
const input = { ...this.config.use_blueprint.input, [key]: value };
if (value === "" || value === undefined) {
delete input[key];
}
fireEvent(this, "value-changed", { fireEvent(this, "value-changed", {
value: { value: {
...this.config!, ...this.config!,
use_blueprint: { use_blueprint: {
...this.config.use_blueprint, ...this.config.use_blueprint,
input: { ...this.config.use_blueprint.input, [key]: value }, input,
}, },
}, },
}); });
@ -264,6 +267,9 @@ export class HaBlueprintAutomationEditor extends LitElement {
.padding { .padding {
padding: 16px; padding: 16px;
} }
.pre-line {
white-space: pre-line;
}
.blueprint-picker-container { .blueprint-picker-container {
padding: 16px; padding: 16px;
} }

View File

@ -12,6 +12,7 @@ import {
internalProperty, internalProperty,
query, query,
TemplateResult, TemplateResult,
css,
} from "lit-element"; } from "lit-element";
import "../../../components/ha-dialog"; import "../../../components/ha-dialog";
import { haStyleDialog } from "../../../resources/styles"; import { haStyleDialog } from "../../../resources/styles";
@ -73,7 +74,9 @@ class DialogImportBlueprint extends LitElement {
this._result.blueprint.metadata.domain this._result.blueprint.metadata.domain
)} )}
<br /> <br />
${this._result.blueprint.metadata.description} <p class="pre-line">
${this._result.blueprint.metadata.description}
</p>
${this._result.validation_errors ${this._result.validation_errors
? html` ? html`
<p class="error"> <p class="error">
@ -199,8 +202,15 @@ class DialogImportBlueprint extends LitElement {
} }
} }
static get styles(): CSSResult { static get styles(): CSSResult[] {
return haStyleDialog; return [
haStyleDialog,
css`
.pre-line {
white-space: pre-line;
}
`,
];
} }
} }

View File

@ -195,7 +195,10 @@ class HaBlueprintOverview extends LitElement {
${this.hass.localize("ui.panel.config.blueprint.overview.introduction")} ${this.hass.localize("ui.panel.config.blueprint.overview.introduction")}
<p> <p>
<a <a
href="${documentationUrl(this.hass, "/docs/blueprint/editor/")}" href="${documentationUrl(
this.hass,
"/docs/automation/using_blueprints/"
)}"
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
> >

View File

@ -328,6 +328,7 @@
"entity-picker": { "entity-picker": {
"entity": "Entity", "entity": "Entity",
"clear": "Clear", "clear": "Clear",
"no_match": "No matching entities found",
"show_entities": "Show entities" "show_entities": "Show entities"
}, },
"entity-attribute-picker": { "entity-attribute-picker": {
@ -359,6 +360,8 @@
"clear": "Clear", "clear": "Clear",
"toggle": "Toggle", "toggle": "Toggle",
"show_devices": "Show devices", "show_devices": "Show devices",
"no_devices": "You don't have any devices",
"no_match": "No matching devices found",
"device": "Device", "device": "Device",
"no_area": "No area" "no_area": "No area"
}, },
@ -367,6 +370,8 @@
"show_areas": "Show areas", "show_areas": "Show areas",
"area": "Area", "area": "Area",
"add_new": "Add new area…", "add_new": "Add new area…",
"no_areas": "You don't have any areas",
"no_match": "No matching areas found",
"add_dialog": { "add_dialog": {
"title": "Add new area", "title": "Add new area",
"text": "Enter the name of the new area.", "text": "Enter the name of the new area.",
@ -1456,8 +1461,8 @@
"description": "Manage blueprints", "description": "Manage blueprints",
"overview": { "overview": {
"header": "Blueprint Editor", "header": "Blueprint Editor",
"introduction": "The blueprint editor allows you to create and edit blueprints.", "introduction": "The blueprint configuration allows you to import and manage your blueprints.",
"learn_more": "Learn more about blueprints", "learn_more": "Learn more about using blueprints",
"headers": { "headers": {
"name": "Name", "name": "Name",
"domain": "Domain", "domain": "Domain",