mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 17:56:46 +00:00
Refactor quick bar to use a common interface for future commands and easier sorting (#7368)
This commit is contained in:
parent
e603893d77
commit
16984d18bb
@ -15,7 +15,7 @@ import {
|
|||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import "../../components/ha-dialog";
|
import "../../components/ha-dialog";
|
||||||
import { haStyleDialog } from "../../resources/styles";
|
import { haStyleDialog } from "../../resources/styles";
|
||||||
import { HomeAssistant, ServiceCallRequest } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { PolymerChangedEvent } from "../../polymer-types";
|
import { PolymerChangedEvent } from "../../polymer-types";
|
||||||
import { fuzzySequentialMatch } from "../../common/string/sequence_matching";
|
import { fuzzySequentialMatch } from "../../common/string/sequence_matching";
|
||||||
import { componentsWithService } from "../../common/config/components_with_service";
|
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 { computeDomain } from "../../common/entity/compute_domain";
|
||||||
import { domainToName } from "../../data/integration";
|
import { domainToName } from "../../data/integration";
|
||||||
import { QuickBarParams } from "./show-dialog-quick-bar";
|
import { QuickBarParams } from "./show-dialog-quick-bar";
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
|
||||||
import { compare } from "../../common/string/compare";
|
import { compare } from "../../common/string/compare";
|
||||||
import { SingleSelectedEvent } from "@material/mwc-list/mwc-list-foundation";
|
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;
|
text: string;
|
||||||
|
altText?: string;
|
||||||
|
icon: string;
|
||||||
|
action(data?: any): void;
|
||||||
|
score?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-quick-bar")
|
@customElement("ha-quick-bar")
|
||||||
export class QuickBar extends LitElement {
|
export class QuickBar extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@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 = "";
|
@internalProperty() private _itemFilter = "";
|
||||||
|
|
||||||
@ -57,9 +63,7 @@ export class QuickBar extends LitElement {
|
|||||||
this._commandMode = params.commandMode || false;
|
this._commandMode = params.commandMode || false;
|
||||||
this._opened = true;
|
this._opened = true;
|
||||||
this._commandItems = this._generateCommandItems();
|
this._commandItems = this._generateCommandItems();
|
||||||
this._entities = Object.keys(this.hass.states).map<HassEntity>(
|
this._entityItems = this._generateEntityItems();
|
||||||
(entity_id) => this.hass.states[entity_id]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeDialog() {
|
public closeDialog() {
|
||||||
@ -91,84 +95,46 @@ export class QuickBar extends LitElement {
|
|||||||
@keydown=${this._handleInputKeyDown}
|
@keydown=${this._handleInputKeyDown}
|
||||||
@focus=${this._resetActivatedIndex}
|
@focus=${this._resetActivatedIndex}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
${this._commandMode
|
<mwc-list activatable @selected=${this.processItemAndCloseDialog}>
|
||||||
? this.renderCommandsList()
|
${this._items.map(
|
||||||
: this.renderEntityList()}
|
(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>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected renderCommandsList() {
|
private async processItemAndCloseDialog(ev: SingleSelectedEvent) {
|
||||||
const items = this._filterCommandItems(
|
const index = ev.detail.index;
|
||||||
this._commandItems,
|
const item = (ev.target as any).items[index].item;
|
||||||
this._itemFilter
|
|
||||||
);
|
|
||||||
|
|
||||||
return html`
|
this._commandTriggered = index;
|
||||||
<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>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected renderEntityList() {
|
await item.action();
|
||||||
const entities = this._filterEntityItems(this._itemFilter);
|
this.closeDialog();
|
||||||
|
|
||||||
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>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _resetActivatedIndex() {
|
private _resetActivatedIndex() {
|
||||||
@ -213,9 +179,23 @@ export class QuickBar extends LitElement {
|
|||||||
this._commandMode = false;
|
this._commandMode = false;
|
||||||
this._itemFilter = newFilter;
|
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();
|
const reloadableDomains = componentsWithService(this.hass, "reload").sort();
|
||||||
|
|
||||||
return reloadableDomains.map((domain) => ({
|
return reloadableDomains.map((domain) => ({
|
||||||
@ -226,69 +206,18 @@ export class QuickBar extends LitElement {
|
|||||||
"domain",
|
"domain",
|
||||||
domainToName(this.hass.localize, domain)
|
domainToName(this.hass.localize, domain)
|
||||||
),
|
),
|
||||||
domain,
|
icon: domainIcon(domain),
|
||||||
service: "reload",
|
action: () => this.hass.callService(domain, "reload"),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _filterCommandItems(
|
private _generateEntityItems(): QuickBarItem[] {
|
||||||
items: CommandItem[],
|
return Object.keys(this.hass.states).map((entityId) => ({
|
||||||
filter: string
|
text: computeStateName(this.hass.states[entityId]),
|
||||||
): CommandItem[] {
|
altText: entityId,
|
||||||
return items
|
icon: domainIcon(computeDomain(entityId), this.hass.states[entityId]),
|
||||||
.filter(({ text }) =>
|
action: () => fireEvent(this, "hass-more-info", { entityId }),
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user