ha-frontend/src/components/ha-service-picker.ts

144 lines
3.9 KiB
TypeScript

import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
import { html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event";
import { LocalizeFunc } from "../common/translations/localize";
import { domainToName } from "../data/integration";
import { HomeAssistant } from "../types";
import "./ha-combo-box";
import "./ha-list-item";
import "./ha-service-icon";
import { getServiceIcons } from "../data/icons";
@customElement("ha-service-picker")
class HaServicePicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean }) public disabled = false;
@property() public value?: string;
@state() private _filter?: string;
protected willUpdate() {
if (!this.hasUpdated) {
this.hass.loadBackendTranslation("services");
getServiceIcons(this.hass);
}
}
private _rowRenderer: ComboBoxLitRenderer<{ service: string; name: string }> =
(item) =>
html`<ha-list-item twoline graphic="icon">
<ha-service-icon
slot="graphic"
.hass=${this.hass}
.service=${item.service}
></ha-service-icon>
<span>${item.name}</span>
<span slot="secondary"
>${item.name === item.service ? "" : item.service}</span
>
</ha-list-item>`;
protected render() {
return html`
<ha-combo-box
.hass=${this.hass}
.label=${this.hass.localize("ui.components.service-picker.service")}
.filteredItems=${this._filteredServices(
this.hass.localize,
this.hass.services,
this._filter
)}
.value=${this.value}
.disabled=${this.disabled}
.renderer=${this._rowRenderer}
item-value-path="service"
item-label-path="name"
allow-custom-value
@filter-changed=${this._filterChanged}
@value-changed=${this._valueChanged}
></ha-combo-box>
`;
}
private _services = memoizeOne(
(
localize: LocalizeFunc,
services: HomeAssistant["services"]
): {
service: string;
name: string;
}[] => {
if (!services) {
return [];
}
const result: { service: string; name: string }[] = [];
Object.keys(services)
.sort()
.forEach((domain) => {
const services_keys = Object.keys(services[domain]).sort();
for (const service of services_keys) {
result.push({
service: `${domain}.${service}`,
name: `${domainToName(localize, domain)}: ${
this.hass.localize(
`component.${domain}.services.${service}.name`
) ||
services[domain][service].name ||
service
}`,
});
}
});
return result;
}
);
private _filteredServices = memoizeOne(
(
localize: LocalizeFunc,
services: HomeAssistant["services"],
filter?: string
) => {
if (!services) {
return [];
}
const processedServices = this._services(localize, services);
if (!filter) {
return processedServices;
}
const split_filter = filter.split(" ");
return processedServices.filter((service) => {
const lower_service_name = service.name.toLowerCase();
const lower_service = service.service.toLowerCase();
return split_filter.every(
(f) => lower_service_name.includes(f) || lower_service.includes(f)
);
});
}
);
private _filterChanged(ev: CustomEvent): void {
this._filter = ev.detail.value.toLowerCase();
}
private _valueChanged(ev) {
this.value = ev.detail.value;
fireEvent(this, "change");
fireEvent(this, "value-changed", { value: this.value });
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-service-picker": HaServicePicker;
}
}