mirror of
https://github.com/home-assistant/frontend.git
synced 2025-04-25 05:47:20 +00:00
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:
parent
c65d414b7b
commit
10b8efc5cb
@ -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!,
|
||||
|
@ -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!,
|
||||
|
@ -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";
|
||||
|
163
src/components/ha-button-related-filter-menu.ts
Normal file
163
src/components/ha-button-related-filter-menu.ts
Normal 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;
|
||||
}
|
||||
}
|
@ -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>) {
|
||||
|
@ -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;
|
||||
|
@ -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."
|
||||
|
Loading…
x
Reference in New Issue
Block a user