frontend/src/components/ha-button-related-filter-me...

222 lines
5.9 KiB
TypeScript

import type { Corner } from "@material/mwc-menu";
import "@material/mwc-menu/mwc-menu-surface";
import { mdiFilterVariant } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import { stopPropagation } from "../common/dom/stop_propagation";
import { computeStateName } from "../common/entity/compute_state_name";
import { computeDeviceName } from "../data/device_registry";
import { findRelated, RelatedResult } from "../data/search";
import type { HomeAssistant } from "../types";
import "./device/ha-device-picker";
import "./entity/ha-entity-picker";
import "./ha-area-picker";
import "./ha-icon-button";
declare global {
// for fire event
interface HASSDomEvents {
"related-changed": {
value?: FilterValue;
items?: RelatedResult;
filter?: string;
};
}
}
interface FilterValue {
area?: string;
device?: string;
entity?: string;
}
@customElement("ha-button-related-filter-menu")
export class HaRelatedFilterButtonMenu extends LitElement {
@property() public hass!: HomeAssistant;
@property() public corner: Corner = "BOTTOM_START";
@property({ type: Boolean, reflect: true }) public narrow = false;
@property({ type: Boolean }) public disabled = false;
@property({ attribute: false }) public value?: FilterValue;
/**
* Show no entities of these domains.
* @type {Array}
* @attr exclude-domains
*/
@property({ type: Array, attribute: "exclude-domains" })
public excludeDomains?: string[];
@state() private _open = false;
protected render(): TemplateResult {
return html`
<ha-icon-button
@click=${this._handleClick}
.label=${this.hass.localize("ui.components.related-filter-menu.filter")}
.path=${mdiFilterVariant}
></ha-icon-button>
<mwc-menu-surface
.open=${this._open}
.anchor=${this}
.fullwidth=${this.narrow}
.corner=${this.corner}
@closed=${this._onClosed}
@input=${stopPropagation}
>
<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}
@click=${this._preventDefault}
></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}
@click=${this._preventDefault}
></ha-device-picker>
<ha-entity-picker
.label=${this.hass.localize(
"ui.components.related-filter-menu.filter_by_entity"
)}
.hass=${this.hass}
.value=${this.value?.entity}
.excludeDomains=${this.excludeDomains}
@value-changed=${this._entityPicked}
@click=${this._preventDefault}
></ha-entity-picker>
</mwc-menu-surface>
`;
}
private _handleClick(): void {
if (this.disabled) {
return;
}
this._open = true;
}
private _onClosed(ev): void {
ev.stopPropagation();
this._open = false;
}
private _preventDefault(ev) {
ev.preventDefault();
}
private async _entityPicked(ev: CustomEvent) {
ev.stopPropagation();
const entityId = ev.detail.value;
if (!entityId) {
fireEvent(this, "related-changed", { value: undefined });
return;
}
const filter = this.hass.localize(
"ui.components.related-filter-menu.filtered_by_entity",
{
entity_name: computeStateName(
(ev.currentTarget as any).comboBox.selectedItem
),
}
);
const items = await findRelated(this.hass, "entity", entityId);
fireEvent(this, "related-changed", {
value: { entity: entityId },
filter,
items,
});
}
private async _devicePicked(ev: CustomEvent) {
ev.stopPropagation();
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: computeDeviceName(
(ev.currentTarget as any).comboBox.selectedItem,
this.hass
),
}
);
const items = await findRelated(this.hass, "device", deviceId);
fireEvent(this, "related-changed", {
value: { device: deviceId },
filter,
items,
});
}
private async _areaPicked(ev: CustomEvent) {
ev.stopPropagation();
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(): CSSResultGroup {
return css`
:host {
display: inline-block;
position: relative;
--mdc-menu-min-width: 250px;
}
ha-area-picker,
ha-device-picker,
ha-entity-picker {
display: block;
width: 300px;
padding: 4px 16px;
box-sizing: border-box;
}
ha-area-picker {
padding-top: 16px;
}
ha-entity-picker {
padding-bottom: 16px;
}
:host([narrow]) ha-area-picker,
:host([narrow]) ha-device-picker,
:host([narrow]) ha-entity-picker {
width: 100%;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-button-related-filter-menu": HaRelatedFilterButtonMenu;
}
}