Add filtering to automaton overview (#8736)

* Add filtering to automaton overview

* Update ha-automation-picker.ts

* Update ha-combo-box.ts

* imports

* Rename component

* localize + comments
This commit is contained in:
Bram Kragten 2021-03-29 14:33:48 +02:00 committed by GitHub
parent c65d414b7b
commit 10b8efc5cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 238 additions and 21 deletions

View File

@ -113,7 +113,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
@internalProperty() private _opened?: boolean;
@query("ha-combo-box", true) private _comboBox!: HaComboBox;
@query("ha-combo-box", true) public comboBox!: HaComboBox;
private _init = false;
@ -242,11 +242,11 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
);
public open() {
this._comboBox?.open();
this.comboBox?.open();
}
public focus() {
this._comboBox?.focus();
this.comboBox?.focus();
}
public hassSubscribe(): UnsubscribeFunc[] {
@ -269,7 +269,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
(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.areas!,
this.entities!,

View File

@ -127,7 +127,7 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
@internalProperty() private _opened?: boolean;
@query("vaadin-combo-box-light", true) private _comboBox!: HTMLElement;
@query("vaadin-combo-box-light", true) public comboBox!: HTMLElement;
private _init = false;
@ -319,7 +319,7 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
(changedProps.has("_opened") && this._opened)
) {
this._init = true;
(this._comboBox as any).items = this._getAreas(
(this.comboBox as any).items = this._getAreas(
this._areas!,
this._devices!,
this._entities!,

View File

@ -1,4 +1,3 @@
import "@material/mwc-button";
import "@material/mwc-menu";
import type { Corner, Menu } from "@material/mwc-menu";
import {
@ -11,8 +10,6 @@ import {
query,
TemplateResult,
} from "lit-element";
import "./ha-icon-button";
@customElement("ha-button-menu")
export class HaButtonMenu extends LitElement {
@property() public corner: Corner = "TOP_START";

View File

@ -0,0 +1,163 @@
import "@material/mwc-icon-button";
import type { Corner } from "@material/mwc-menu";
import { mdiFilterVariant } from "@mdi/js";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "@material/mwc-menu/mwc-menu-surface";
import { fireEvent } from "../common/dom/fire_event";
import { findRelated, RelatedResult } from "../data/search";
import type { HomeAssistant } from "../types";
import "./ha-svg-icon";
import "./ha-area-picker";
import "./device/ha-device-picker";
declare global {
// for fire event
interface HASSDomEvents {
"related-changed": {
value?: FilterValue;
items?: RelatedResult;
filter?: string;
};
}
}
interface FilterValue {
area?: string;
device?: string;
}
@customElement("ha-button-related-filter-menu")
export class HaRelatedFilterButtonMenu extends LitElement {
@property() public hass!: HomeAssistant;
@property() public corner: Corner = "TOP_START";
@property({ type: Boolean, reflect: true }) public narrow = false;
@property({ type: Boolean }) public disabled = false;
@property({ attribute: false }) public value?: FilterValue;
@internalProperty() private _open = false;
protected render(): TemplateResult {
return html`
<mwc-icon-button @click=${this._handleClick}>
<ha-svg-icon .path=${mdiFilterVariant}></ha-svg-icon>
</mwc-icon-button>
<mwc-menu-surface
.open=${this._open}
.anchor=${this}
.fullwidth=${this.narrow}
.corner=${this.corner}
@closed=${this._onClosed}
>
<ha-area-picker
.label=${this.hass.localize(
"ui.components.related-filter-menu.filter_by_area"
)}
.hass=${this.hass}
.value=${this.value?.area}
no-add
@value-changed=${this._areaPicked}
></ha-area-picker>
<ha-device-picker
.label=${this.hass.localize(
"ui.components.related-filter-menu.filter_by_device"
)}
.hass=${this.hass}
.value=${this.value?.device}
@value-changed=${this._devicePicked}
></ha-device-picker>
</mwc-menu-surface>
`;
}
private _handleClick(): void {
if (this.disabled) {
return;
}
this._open = true;
}
private _onClosed(): void {
this._open = false;
}
private async _devicePicked(ev: CustomEvent) {
const deviceId = ev.detail.value;
if (!deviceId) {
fireEvent(this, "related-changed", { value: undefined });
return;
}
const filter = this.hass.localize(
"ui.components.related-filter-menu.filtered_by_device",
"device_name",
(ev.currentTarget as any).comboBox.selectedItem.name
);
const items = await findRelated(this.hass, "device", deviceId);
fireEvent(this, "related-changed", {
value: { device: deviceId },
filter,
items,
});
}
private async _areaPicked(ev: CustomEvent) {
const areaId = ev.detail.value;
if (!areaId) {
fireEvent(this, "related-changed", { value: undefined });
return;
}
const filter = this.hass.localize(
"ui.components.related-filter-menu.filtered_by_area",
"area_name",
(ev.currentTarget as any).comboBox.selectedItem.name
);
const items = await findRelated(this.hass, "area", areaId);
fireEvent(this, "related-changed", {
value: { area: areaId },
filter,
items,
});
}
static get styles(): CSSResult {
return css`
:host {
display: inline-block;
position: relative;
}
:host([narrow]) {
position: static;
}
ha-area-picker,
ha-device-picker {
display: block;
width: 300px;
padding: 4px 16px;
box-sizing: border-box;
}
:host([narrow]) ha-area-picker,
:host([narrow]) ha-device-picker {
width: 100%;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-button-related-filter-menu": HaRelatedFilterButtonMenu;
}
}

View File

@ -86,6 +86,10 @@ export class HaComboBox extends LitElement {
});
}
public get selectedItem() {
return (this._comboBox as any).selectedItem;
}
protected render(): TemplateResult {
return html`
<vaadin-combo-box-light
@ -149,9 +153,9 @@ export class HaComboBox extends LitElement {
fireEvent(this, ev.type, ev.detail);
}
private _filterChanged(ev: PolymerChangedEvent<boolean>) {
private _filterChanged(ev: PolymerChangedEvent<string>) {
// @ts-ignore
fireEvent(this, ev.type, ev.detail);
fireEvent(this, ev.type, ev.detail, { composed: false });
}
private _valueChanged(ev: PolymerChangedEvent<string>) {

View File

@ -5,6 +5,7 @@ import {
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
@ -20,6 +21,7 @@ import { DataTableColumnContainer } from "../../../components/data-table/ha-data
import "../../../components/entity/ha-entity-toggle";
import "../../../components/ha-fab";
import "../../../components/ha-svg-icon";
import "../../../components/ha-button-related-filter-menu";
import {
AutomationEntity,
triggerAutomationActions,
@ -45,14 +47,33 @@ class HaAutomationPicker extends LitElement {
@property() public automations!: AutomationEntity[];
private _automations = memoizeOne((automations: AutomationEntity[]) => {
return automations.map((automation) => {
return {
...automation,
name: computeStateName(automation),
};
});
});
@property() private _activeFilters?: string[];
@internalProperty() private _filteredAutomations?: string[] | null;
@internalProperty() private _filterValue?;
private _automations = memoizeOne(
(
automations: AutomationEntity[],
filteredAutomations?: string[] | null
) => {
if (filteredAutomations === null) {
return [];
}
return (filteredAutomations
? automations.filter((automation) =>
filteredAutomations!.includes(automation.entity_id)
)
: automations
).map((automation) => {
return {
...automation,
name: computeStateName(automation),
};
});
}
);
private _columns = memoizeOne(
(narrow: boolean, _locale): DataTableColumnContainer => {
@ -192,17 +213,27 @@ class HaAutomationPicker extends LitElement {
back-path="/config"
.route=${this.route}
.tabs=${configSections.automation}
.activeFilters=${this._activeFilters}
.columns=${this._columns(this.narrow, this.hass.locale)}
.data=${this._automations(this.automations)}
id="entity_id"
.data=${this._automations(this.automations, this._filteredAutomations)}
.noDataText=${this.hass.localize(
"ui.panel.config.automation.picker.no_automations"
)}
@clear-filter=${this._clearFilter}
hasFab
>
<mwc-icon-button slot="toolbar-icon" @click=${this._showHelp}>
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
</mwc-icon-button>
<ha-button-related-filter-menu
slot="filter-menu"
corner="BOTTOM_START"
.narrow=${this.narrow}
.hass=${this.hass}
.value=${this._filterValue}
@related-changed=${this._relatedFilterChanged}
>
</ha-button-related-filter-menu>
<ha-fab
slot="fab"
.label=${this.hass.localize(
@ -217,6 +248,22 @@ class HaAutomationPicker extends LitElement {
`;
}
private _relatedFilterChanged(ev: CustomEvent) {
this._filterValue = ev.detail.value;
if (!this._filterValue) {
this._clearFilter();
return;
}
this._activeFilters = [ev.detail.filter];
this._filteredAutomations = ev.detail.items.automation || null;
}
private _clearFilter() {
this._filteredAutomations = undefined;
this._activeFilters = undefined;
this._filterValue = undefined;
}
private _showInfo(ev) {
ev.stopPropagation();
const entityId = ev.currentTarget.automation.entity_id;

View File

@ -398,6 +398,12 @@
}
}
},
"related-filter-menu": {
"filter_by_device": "Filter by device",
"filter_by_area": "Filter by area",
"filtered_by_device": "device: {device_name}",
"filtered_by_area": "area: {area_name}"
},
"picture-upload": {
"label": "Picture",
"unsupported_format": "Unsupported format, please choose a JPEG, PNG or GIF image."