Refactor quick bar to use a common interface for future commands and easier sorting (#7368)

This commit is contained in:
Donnie 2020-10-17 15:48:48 -07:00 committed by GitHub
parent e603893d77
commit 16984d18bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -15,7 +15,7 @@ import {
import { fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-dialog";
import { haStyleDialog } from "../../resources/styles";
import { HomeAssistant, ServiceCallRequest } from "../../types";
import { HomeAssistant } from "../../types";
import { PolymerChangedEvent } from "../../polymer-types";
import { fuzzySequentialMatch } from "../../common/string/sequence_matching";
import { componentsWithService } from "../../common/config/components_with_service";
@ -23,21 +23,27 @@ import { domainIcon } from "../../common/entity/domain_icon";
import { computeDomain } from "../../common/entity/compute_domain";
import { domainToName } from "../../data/integration";
import { QuickBarParams } from "./show-dialog-quick-bar";
import { HassEntity } from "home-assistant-js-websocket";
import { compare } from "../../common/string/compare";
import { SingleSelectedEvent } from "@material/mwc-list/mwc-list-foundation";
import { computeStateName } from "../../common/entity/compute_state_name";
interface CommandItem extends ServiceCallRequest {
interface QuickBarItem {
text: string;
altText?: string;
icon: string;
action(data?: any): void;
score?: number;
}
@customElement("ha-quick-bar")
export class QuickBar extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@internalProperty() private _commandItems: CommandItem[] = [];
@internalProperty() private _commandItems: QuickBarItem[] = [];
@internalProperty() private _entities: HassEntity[] = [];
@internalProperty() private _entityItems: QuickBarItem[] = [];
@internalProperty() private _items: QuickBarItem[] = [];
@internalProperty() private _itemFilter = "";
@ -57,9 +63,7 @@ export class QuickBar extends LitElement {
this._commandMode = params.commandMode || false;
this._opened = true;
this._commandItems = this._generateCommandItems();
this._entities = Object.keys(this.hass.states).map<HassEntity>(
(entity_id) => this.hass.states[entity_id]
);
this._entityItems = this._generateEntityItems();
}
public closeDialog() {
@ -91,84 +95,46 @@ export class QuickBar extends LitElement {
@keydown=${this._handleInputKeyDown}
@focus=${this._resetActivatedIndex}
></paper-input>
${this._commandMode
? this.renderCommandsList()
: this.renderEntityList()}
<mwc-list activatable @selected=${this.processItemAndCloseDialog}>
${this._items.map(
(item, index) => html`
<mwc-list-item
.twoline=${Boolean(item.altText)}
.activated=${index === this._activatedIndex}
.item=${item}
.index=${index}
@keydown=${this._handleListItemKeyDown}
hasMeta
graphic=${item.altText ? "avatar" : "icon"}
>
<ha-icon .icon=${item.icon} slot="graphic"></ha-icon>
<span>${item.text}</span>
${item.altText
? html` <span slot="secondary">${item.altText}</span> `
: null}
${this._commandTriggered === index
? html`<ha-circular-progress
size="small"
active
slot="meta"
></ha-circular-progress>`
: null}
</mwc-list-item>
`
)}
</mwc-list>
</ha-dialog>
`;
}
protected renderCommandsList() {
const items = this._filterCommandItems(
this._commandItems,
this._itemFilter
);
private async processItemAndCloseDialog(ev: SingleSelectedEvent) {
const index = ev.detail.index;
const item = (ev.target as any).items[index].item;
return html`
<mwc-list activatable @selected=${this._processCommand}>
${items.map(
(item, index) => html`
<mwc-list-item
.activated=${index === this._activatedIndex}
.item=${item}
.index=${index}
@keydown=${this._handleListItemKeyDown}
hasMeta
graphic="icon"
>
<ha-icon
.icon=${domainIcon(item.domain)}
slot="graphic"
></ha-icon>
${item.text}
${this._commandTriggered === index
? html`<ha-circular-progress
size="small"
active
slot="meta"
></ha-circular-progress>`
: null}
</mwc-list-item>
`
)}
</mwc-list>
`;
}
this._commandTriggered = index;
protected renderEntityList() {
const entities = this._filterEntityItems(this._itemFilter);
return html`
<mwc-list activatable @selected=${this._entityMoreInfo}>
${entities.map((entity, index) => {
const domain = computeDomain(entity.entity_id);
return html`
<mwc-list-item
twoline
.entityId=${entity.entity_id}
graphic="avatar"
.activated=${index === this._activatedIndex}
.index=${index}
@keydown=${this._handleListItemKeyDown}
>
<ha-icon .icon=${domainIcon(domain)} slot="graphic"></ha-icon>
${entity.attributes?.friendly_name
? html`
<span>
${entity.attributes?.friendly_name}
</span>
<span slot="secondary">${entity.entity_id}</span>
`
: html`
<span>
${entity.entity_id}
</span>
`}
</mwc-list-item>
`;
})}
</mwc-list>
`;
await item.action();
this.closeDialog();
}
private _resetActivatedIndex() {
@ -213,9 +179,23 @@ export class QuickBar extends LitElement {
this._commandMode = false;
this._itemFilter = newFilter;
}
this._items = (this._commandMode ? this._commandItems : this._entityItems)
.filter(({ text, altText }) => {
const values = [text];
if (altText) {
values.push(altText);
}
return fuzzySequentialMatch(this._itemFilter, values);
})
.sort((itemA, itemB) => compare(itemA.text, itemB.text));
}
private _generateCommandItems(): CommandItem[] {
private _generateCommandItems(): QuickBarItem[] {
return [...this._generateReloadCommands()];
}
private _generateReloadCommands(): QuickBarItem[] {
const reloadableDomains = componentsWithService(this.hass, "reload").sort();
return reloadableDomains.map((domain) => ({
@ -226,69 +206,18 @@ export class QuickBar extends LitElement {
"domain",
domainToName(this.hass.localize, domain)
),
domain,
service: "reload",
icon: domainIcon(domain),
action: () => this.hass.callService(domain, "reload"),
}));
}
private _filterCommandItems(
items: CommandItem[],
filter: string
): CommandItem[] {
return items
.filter(({ text }) =>
fuzzySequentialMatch(filter.toLowerCase(), [text.toLowerCase()])
)
.sort((itemA, itemB) => compare(itemA.text, itemB.text));
}
private _filterEntityItems(filter: string): HassEntity[] {
return this._entities
.filter(({ entity_id, attributes: { friendly_name } }) => {
const values = [entity_id];
if (friendly_name) {
values.push(friendly_name);
}
return fuzzySequentialMatch(filter.toLowerCase(), values);
})
.sort((entityA, entityB) =>
compare(entityA.entity_id, entityB.entity_id)
);
}
private async _processCommand(ev: SingleSelectedEvent) {
const index = ev.detail.index;
const item = (ev.target as any).items[index].item;
this._commandTriggered = index;
this._runCommandAndCloseDialog({
domain: item.domain,
service: item.service,
serviceData: item.serviceData,
});
}
private async _runCommandAndCloseDialog(request?: ServiceCallRequest) {
if (!request) {
return;
}
this.hass
.callService(request.domain, request.service, request.serviceData)
.then(() => this.closeDialog());
}
private _entityMoreInfo(ev: SingleSelectedEvent) {
const index = ev.detail.index;
const entityId = (ev.target as any).items[index].entityId;
this._launchMoreInfoDialog(entityId);
}
private _launchMoreInfoDialog(entityId) {
fireEvent(this, "hass-more-info", { entityId });
this.closeDialog();
private _generateEntityItems(): QuickBarItem[] {
return Object.keys(this.hass.states).map((entityId) => ({
text: computeStateName(this.hass.states[entityId]),
altText: entityId,
icon: domainIcon(computeDomain(entityId), this.hass.states[entityId]),
action: () => fireEvent(this, "hass-more-info", { entityId }),
}));
}
static get styles() {