Quick Bar: Use command category labels instead of icons (#7692)

* Change commands to use category labels instead of icons. Fixes several translation issues.

* Hydrate with latest dev and resolve conflicts

* Replace custom pill element with material chips

* Add category icons. Fix dark mode text colors

* Update src/components/ha-chip-set.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Update src/dialogs/quick-bar/ha-quick-bar.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Update src/dialogs/quick-bar/ha-quick-bar.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Update src/components/ha-chip.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Update src/components/ha-chip.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Donnie 2021-03-25 08:21:48 -07:00 committed by GitHub
parent 57e535c2c8
commit 6de8b4e35f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 318 additions and 143 deletions

View File

@ -18,7 +18,6 @@ import {
} from "lit-element"; } from "lit-element";
import { cache } from "lit-html/directives/cache"; import { cache } from "lit-html/directives/cache";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-chips";
import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-dialog"; import "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-expansion-panel"; import "../../../../src/components/ha-expansion-panel";

View File

@ -42,7 +42,7 @@ export const fuzzySequentialMatch = (filter: string, ...words: string[]) => {
export interface ScorableTextItem { export interface ScorableTextItem {
score?: number; score?: number;
text: string; filterText: string;
altText?: string; altText?: string;
} }
@ -55,8 +55,8 @@ export const fuzzyFilterSort: FuzzyFilterSort = (filter, items) => {
return items return items
.map((item) => { .map((item) => {
item.score = item.altText item.score = item.altText
? fuzzySequentialMatch(filter, item.text, item.altText) ? fuzzySequentialMatch(filter, item.filterText, item.altText)
: fuzzySequentialMatch(filter, item.text); : fuzzySequentialMatch(filter, item.filterText);
return item; return item;
}) })
.filter((item) => item.score !== undefined && item.score > 0) .filter((item) => item.score !== undefined && item.score > 0)

View File

@ -1,6 +1,5 @@
// @ts-ignore // @ts-ignore
import chipStyles from "@material/chips/dist/mdc.chips.min.css"; import chipStyles from "@material/chips/dist/mdc.chips.min.css";
import { ripple } from "@material/mwc-ripple/ripple-directive";
import { import {
css, css,
CSSResult, CSSResult,
@ -12,6 +11,7 @@ import {
unsafeCSS, unsafeCSS,
} from "lit-element"; } from "lit-element";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import "./ha-chip";
declare global { declare global {
// for fire event // for fire event
@ -20,8 +20,8 @@ declare global {
} }
} }
@customElement("ha-chips") @customElement("ha-chip-set")
export class HaChips extends LitElement { export class HaChipSet extends LitElement {
@property() public items = []; @property() public items = [];
protected render(): TemplateResult { protected render(): TemplateResult {
@ -33,18 +33,9 @@ export class HaChips extends LitElement {
${this.items.map( ${this.items.map(
(item, idx) => (item, idx) =>
html` html`
<div class="mdc-chip" .index=${idx} @click=${this._handleClick}> <ha-chip .index=${idx} @click=${this._handleClick}>
<div class="mdc-chip__ripple" .ripple="${ripple()}"></div> ${item}
<span role="gridcell"> </ha-chip>
<span
role="button"
tabindex="0"
class="mdc-chip__primary-action"
>
<span class="mdc-chip__text">${item}</span>
</span>
</span>
</div>
` `
)} )}
</div> </div>
@ -60,16 +51,12 @@ export class HaChips extends LitElement {
static get styles(): CSSResult { static get styles(): CSSResult {
return css` return css`
${unsafeCSS(chipStyles)} ${unsafeCSS(chipStyles)}
.mdc-chip {
background-color: rgba(var(--rgb-primary-text-color), 0.15);
color: var(--primary-text-color);
}
`; `;
} }
} }
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"ha-chips": HaChips; "ha-chip-set": HaChipSet;
} }
} }

75
src/components/ha-chip.ts Normal file
View File

@ -0,0 +1,75 @@
// @ts-ignore
import chipStyles from "@material/chips/dist/mdc.chips.min.css";
import { ripple } from "@material/mwc-ripple/ripple-directive";
import "./ha-icon";
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
unsafeCSS,
} from "lit-element";
declare global {
// for fire event
interface HASSDomEvents {
"chip-clicked": { index: string };
}
}
@customElement("ha-chip")
export class HaChip extends LitElement {
@property() public index = 0;
@property({type: Boolean}) public hasIcon = false;
protected render(): TemplateResult {
return html`
<div class="mdc-chip" .index=${this.index}>
${this.hasIcon
? html`<div class="mdc-chip__icon mdc-chip__icon--leading">
<slot name="icon"></slot>
</div>`
: null}
<div class="mdc-chip__ripple" .ripple="${ripple()}"></div>
<span role="gridcell">
<span role="button" tabindex="0" class="mdc-chip__primary-action">
<span class="mdc-chip__text"><slot></slot></span>
</span>
</span>
</div>
`;
}
static get styles(): CSSResult {
return css`
${unsafeCSS(chipStyles)}
.mdc-chip {
margin: 4px;
background-color: var(
--ha-chip-background-color,
rgba(var(--rgb-primary-text-color), 0.15)
);
color: var(--ha-chip-text-color, var(--primary-text-color));
}
.mdc-chip:hover {
color: var(--ha-chip-text-color, var(--primary-text-color));
}
.mdc-chip__icon--leading {
--mdc-icon-size: 20px;
color: var(--ha-chip-icon-color, var(--ha-chip-text-color));
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-chip": HaChip;
}
}

View File

@ -3,7 +3,7 @@ import type { List } from "@material/mwc-list/mwc-list";
import { SingleSelectedEvent } from "@material/mwc-list/mwc-list-foundation"; import { SingleSelectedEvent } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import type { ListItem } from "@material/mwc-list/mwc-list-item"; import type { ListItem } from "@material/mwc-list/mwc-list-item";
import { mdiConsoleLine } from "@mdi/js"; import { mdiConsoleLine, mdiEarth, mdiReload, mdiServerNetwork } from "@mdi/js";
import { import {
css, css,
customElement, customElement,
@ -36,7 +36,7 @@ import "../../components/ha-circular-progress";
import "../../components/ha-dialog"; import "../../components/ha-dialog";
import "../../components/ha-header-bar"; import "../../components/ha-header-bar";
import { domainToName } from "../../data/integration"; import { domainToName } from "../../data/integration";
import { getPanelIcon, getPanelNameTranslationKey } from "../../data/panel"; import { getPanelNameTranslationKey } from "../../data/panel";
import { PageNavigation } from "../../layouts/hass-tabs-subpage"; import { PageNavigation } from "../../layouts/hass-tabs-subpage";
import { configSections } from "../../panels/config/ha-panel-config"; import { configSections } from "../../panels/config/ha-panel-config";
import { haStyleDialog } from "../../resources/styles"; import { haStyleDialog } from "../../resources/styles";
@ -46,31 +46,44 @@ import {
showConfirmationDialog, showConfirmationDialog,
} from "../generic/show-dialog-box"; } from "../generic/show-dialog-box";
import { QuickBarParams } from "./show-dialog-quick-bar"; import { QuickBarParams } from "./show-dialog-quick-bar";
import "../../components/ha-chip";
const DEFAULT_NAVIGATION_ICON = "hass:arrow-right-circle";
const DEFAULT_SERVER_ICON = "hass:server";
interface QuickBarItem extends ScorableTextItem { interface QuickBarItem extends ScorableTextItem {
icon?: string; primaryText: string;
iconPath?: string; iconPath?: string;
action(data?: any): void; action(data?: any): void;
} }
interface QuickBarNavigationItem extends QuickBarItem { interface CommandItem extends QuickBarItem {
categoryKey: "reload" | "navigation" | "server_control";
categoryText: string;
}
interface EntityItem extends QuickBarItem {
icon?: string;
}
const isCommandItem = (item: EntityItem | CommandItem): item is CommandItem => {
return (item as CommandItem).categoryKey !== undefined;
};
interface QuickBarNavigationItem extends CommandItem {
path: string; path: string;
} }
interface NavigationInfo extends PageNavigation { type NavigationInfo = PageNavigation & Pick<QuickBarItem, "primaryText">;
text: string;
}
type BaseNavigationCommand = Pick<
QuickBarNavigationItem,
"primaryText" | "path"
>;
@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?: QuickBarItem[]; @internalProperty() private _commandItems?: CommandItem[];
@internalProperty() private _entityItems?: QuickBarItem[]; @internalProperty() private _entityItems?: EntityItem[];
@internalProperty() private _items?: QuickBarItem[] = []; @internalProperty() private _items?: QuickBarItem[] = [];
@ -201,6 +214,12 @@ export class QuickBar extends LitElement {
} }
private _renderItem(item: QuickBarItem, index?: number) { private _renderItem(item: QuickBarItem, index?: number) {
return isCommandItem(item)
? this._renderCommandItem(item, index)
: this._renderEntityItem(item, index);
}
private _renderEntityItem(item: EntityItem, index?: number) {
return html` return html`
<mwc-list-item <mwc-list-item
.twoline=${Boolean(item.altText)} .twoline=${Boolean(item.altText)}
@ -213,10 +232,50 @@ export class QuickBar extends LitElement {
${item.iconPath ${item.iconPath
? html`<ha-svg-icon ? html`<ha-svg-icon
.path=${item.iconPath} .path=${item.iconPath}
class="entity"
slot="graphic" slot="graphic"
></ha-svg-icon>` ></ha-svg-icon>`
: html`<ha-icon .icon=${item.icon} slot="graphic"></ha-icon>`} : html`<ha-icon
${item.text} .icon=${item.icon}
class="entity"
slot="graphic"
></ha-icon>`}
<span>${item.primaryText}</span>
${item.altText
? html`
<span slot="secondary" class="item-text secondary"
>${item.altText}</span
>
`
: null}
</mwc-list-item>
`;
}
private _renderCommandItem(item: CommandItem, index?: number) {
return html`
<mwc-list-item
.twoline=${Boolean(item.altText)}
.item=${item}
index=${ifDefined(index)}
>
<span>
<ha-chip
.label="${item.categoryText}"
hasIcon
class="command-category ${item.categoryKey}"
>
${item.iconPath
? html`<ha-svg-icon
.path=${item.iconPath}
slot="icon"
></ha-svg-icon>`
: ""}
${item.categoryText}</ha-chip
>
</span>
<span>${item.primaryText}</span>
${item.altText ${item.altText
? html` ? html`
<span slot="secondary" class="item-text secondary" <span slot="secondary" class="item-text secondary"
@ -315,94 +374,112 @@ export class QuickBar extends LitElement {
private _generateEntityItems(): QuickBarItem[] { private _generateEntityItems(): QuickBarItem[] {
return Object.keys(this.hass.states) return Object.keys(this.hass.states)
.map((entityId) => ({ .map((entityId) => {
text: computeStateName(this.hass.states[entityId]), const primaryText = computeStateName(this.hass.states[entityId]);
altText: entityId, return {
icon: domainIcon(computeDomain(entityId), this.hass.states[entityId]), primaryText,
action: () => fireEvent(this, "hass-more-info", { entityId }), filterText: primaryText,
})) altText: entityId,
.sort((a, b) => compare(a.text.toLowerCase(), b.text.toLowerCase())); icon: domainIcon(computeDomain(entityId), this.hass.states[entityId]),
action: () => fireEvent(this, "hass-more-info", { entityId }),
};
})
.sort((a, b) =>
compare(a.primaryText.toLowerCase(), b.primaryText.toLowerCase())
);
} }
private _generateCommandItems(): QuickBarItem[] { private _generateCommandItems(): CommandItem[] {
return [ return [
...this._generateReloadCommands(), ...this._generateReloadCommands(),
...this._generateServerControlCommands(), ...this._generateServerControlCommands(),
...this._generateNavigationCommands(), ...this._generateNavigationCommands(),
].sort((a, b) => compare(a.text.toLowerCase(), b.text.toLowerCase())); ].sort((a, b) =>
compare(a.filterText.toLowerCase(), b.filterText.toLowerCase())
);
} }
private _generateReloadCommands(): QuickBarItem[] { private _generateReloadCommands(): CommandItem[] {
const reloadableDomains = componentsWithService(this.hass, "reload").sort(); const reloadableDomains = componentsWithService(this.hass, "reload").sort();
return reloadableDomains.map((domain) => ({ return reloadableDomains.map((domain) => {
text: const categoryText = this.hass.localize(
`ui.dialogs.quick-bar.commands.types.reload`
);
const primaryText =
this.hass.localize(`ui.dialogs.quick-bar.commands.reload.${domain}`) || this.hass.localize(`ui.dialogs.quick-bar.commands.reload.${domain}`) ||
this.hass.localize( this.hass.localize(
"ui.dialogs.quick-bar.commands.reload.reload", "ui.dialogs.quick-bar.commands.reload.reload",
"domain", "domain",
domainToName(this.hass.localize, domain) domainToName(this.hass.localize, domain)
), );
icon: domainIcon(domain),
action: () => this.hass.callService(domain, "reload"), return {
})); primaryText,
filterText: `${categoryText} ${primaryText}`,
action: () => this.hass.callService(domain, "reload"),
categoryKey: "reload",
iconPath: mdiReload,
categoryText,
};
});
} }
private _generateServerControlCommands(): QuickBarItem[] { private _generateServerControlCommands(): CommandItem[] {
const serverActions = ["restart", "stop"]; const serverActions = ["restart", "stop"];
return serverActions.map((action) => return serverActions.map((action) => {
this._generateConfirmationCommand( const categoryKey = "server_control";
const categoryText = this.hass.localize(
`ui.dialogs.quick-bar.commands.types.${categoryKey}`
);
const primaryText = this.hass.localize(
"ui.dialogs.quick-bar.commands.server_control.perform_action",
"action",
this.hass.localize(
`ui.dialogs.quick-bar.commands.server_control.${action}`
)
);
return this._generateConfirmationCommand(
{ {
text: this.hass.localize( primaryText,
"ui.dialogs.quick-bar.commands.server_control.perform_action", filterText: `${categoryText} ${primaryText}`,
"action", categoryKey,
this.hass.localize( iconPath: mdiServerNetwork,
`ui.dialogs.quick-bar.commands.server_control.${action}` categoryText,
)
),
icon: DEFAULT_SERVER_ICON,
action: () => this.hass.callService("homeassistant", action), action: () => this.hass.callService("homeassistant", action),
}, },
this.hass.localize("ui.dialogs.generic.ok") this.hass.localize("ui.dialogs.generic.ok")
) );
); });
} }
private _generateNavigationCommands(): QuickBarItem[] { private _generateNavigationCommands(): CommandItem[] {
const panelItems = this._generateNavigationPanelCommands(); const panelItems = this._generateNavigationPanelCommands();
const sectionItems = this._generateNavigationConfigSectionCommands(); const sectionItems = this._generateNavigationConfigSectionCommands();
return this._withNavigationActions([...panelItems, ...sectionItems]); return this._finalizeNavigationCommands([...panelItems, ...sectionItems]);
} }
private _generateNavigationPanelCommands(): Omit< private _generateNavigationPanelCommands(): BaseNavigationCommand[] {
QuickBarNavigationItem,
"action"
>[] {
return Object.keys(this.hass.panels) return Object.keys(this.hass.panels)
.filter((panelKey) => panelKey !== "_my_redirect") .filter((panelKey) => panelKey !== "_my_redirect")
.map((panelKey) => { .map((panelKey) => {
const panel = this.hass.panels[panelKey]; const panel = this.hass.panels[panelKey];
const translationKey = getPanelNameTranslationKey(panel); const translationKey = getPanelNameTranslationKey(panel);
const text = this.hass.localize( const primaryText =
"ui.dialogs.quick-bar.commands.navigation.navigate_to", this.hass.localize(translationKey) || panel.title || panel.url_path;
"panel",
this.hass.localize(translationKey) || panel.title || panel.url_path
);
return { return {
text, primaryText,
icon: getPanelIcon(panel) || DEFAULT_NAVIGATION_ICON,
path: `/${panel.url_path}`, path: `/${panel.url_path}`,
}; };
}); });
} }
private _generateNavigationConfigSectionCommands(): Partial< private _generateNavigationConfigSectionCommands(): BaseNavigationCommand[] {
QuickBarNavigationItem
>[] {
const items: NavigationInfo[] = []; const items: NavigationInfo[] = [];
for (const sectionKey of Object.keys(configSections)) { for (const sectionKey of Object.keys(configSections)) {
@ -426,18 +503,12 @@ export class QuickBar extends LitElement {
page: PageNavigation page: PageNavigation
): NavigationInfo | undefined { ): NavigationInfo | undefined {
if (page.component) { if (page.component) {
const shortCaption = this.hass.localize( const caption = this.hass.localize(
`ui.dialogs.quick-bar.commands.navigation.${page.component}` `ui.dialogs.quick-bar.commands.navigation.${page.component}`
); );
if (page.translationKey && shortCaption) { if (page.translationKey && caption) {
const caption = this.hass.localize( return { ...page, primaryText: caption };
"ui.dialogs.quick-bar.commands.navigation.navigate_to",
"panel",
shortCaption
);
return { ...page, text: caption };
} }
} }
@ -445,9 +516,9 @@ export class QuickBar extends LitElement {
} }
private _generateConfirmationCommand( private _generateConfirmationCommand(
item: QuickBarItem, item: CommandItem,
confirmText: ConfirmationDialogParams["confirmText"] confirmText: ConfirmationDialogParams["confirmText"]
): QuickBarItem { ): CommandItem {
return { return {
...item, ...item,
action: () => action: () =>
@ -458,13 +529,24 @@ export class QuickBar extends LitElement {
}; };
} }
private _withNavigationActions(items) { private _finalizeNavigationCommands(
return items.map(({ text, icon, iconPath, path }) => ({ items: BaseNavigationCommand[]
text, ): CommandItem[] {
icon, return items.map((item) => {
iconPath, const categoryKey = "navigation";
action: () => navigate(this, path), const categoryText = this.hass.localize(
})); `ui.dialogs.quick-bar.commands.types.${categoryKey}`
);
return {
...item,
categoryKey,
iconPath: mdiEarth,
categoryText,
filterText: `${categoryText} ${item.primaryText}`,
action: () => navigate(this, item.path),
};
});
} }
private _toggleIfAlreadyOpened() { private _toggleIfAlreadyOpened() {
@ -506,8 +588,8 @@ export class QuickBar extends LitElement {
} }
} }
ha-icon, ha-icon.entity,
ha-svg-icon { ha-svg-icon.entity {
margin-left: 20px; margin-left: 20px;
} }
@ -516,6 +598,29 @@ export class QuickBar extends LitElement {
color: var(--primary-text-color); color: var(--primary-text-color);
} }
span.command-category {
font-weight: bold;
padding: 3px;
display: inline-flex;
border-radius: 6px;
color: black;
}
.command-category.reload {
--ha-chip-background-color: #cddc39;
--ha-chip-text-color: black;
}
.command-category.navigation {
--ha-chip-background-color: var(--light-primary-color);
--ha-chip-text-color: black;
}
.command-category.server_control {
--ha-chip-background-color: var(--warning-color);
--ha-chip-text-color: black;
}
.uni-virtualizer-host { .uni-virtualizer-host {
display: block; display: block;
position: relative; position: relative;

View File

@ -7,7 +7,7 @@ import {
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import "../../../../components/ha-card"; import "../../../../components/ha-card";
import "../../../../components/ha-chips"; import "../../../../components/ha-chip-set";
import { showAutomationEditor } from "../../../../data/automation"; import { showAutomationEditor } from "../../../../data/automation";
import { import {
DeviceAction, DeviceAction,
@ -65,13 +65,13 @@ export abstract class HaDeviceAutomationCard<
${this.hass.localize(this.headerKey)} ${this.hass.localize(this.headerKey)}
</h3> </h3>
<div class="content"> <div class="content">
<ha-chips <ha-chip-set
@chip-clicked=${this._handleAutomationClicked} @chip-clicked=${this._handleAutomationClicked}
.items=${this.automations.map((automation) => .items=${this.automations.map((automation) =>
this._localizeDeviceAutomation(this.hass, automation) this._localizeDeviceAutomation(this.hass, automation)
)} )}
> >
</ha-chips> </ha-chip-set>
</div> </div>
`; `;
} }

View File

@ -92,6 +92,7 @@ export const derivedStyles = {
"mdc-button-disabled-ink-color": "var(--disabled-text-color)", "mdc-button-disabled-ink-color": "var(--disabled-text-color)",
"mdc-button-outline-color": "var(--divider-color)", "mdc-button-outline-color": "var(--divider-color)",
"mdc-dialog-scroll-divider-color": "var(--divider-color)", "mdc-dialog-scroll-divider-color": "var(--divider-color)",
"chip-background-color": "rgba(var(--rgb-primary-text-color), 0.15)",
}; };
export const buttonLinkStyle = css` export const buttonLinkStyle = css`

View File

@ -546,12 +546,16 @@
"rpi_gpio": "[%key:ui::panel::config::server_control::section::reloading::rpi_gpio%]" "rpi_gpio": "[%key:ui::panel::config::server_control::section::reloading::rpi_gpio%]"
}, },
"server_control": { "server_control": {
"perform_action": "{action} Server", "perform_action": "{action} server",
"restart": "[%key:ui::panel::config::server_control::section::server_management::restart%]", "restart": "[%key:ui::panel::config::server_control::section::server_management::restart%]",
"stop": "[%key:ui::panel::config::server_control::section::server_management::stop%]" "stop": "[%key:ui::panel::config::server_control::section::server_management::stop%]"
}, },
"types": {
"reload": "Reload",
"navigation": "Navigate",
"server_control": "Server"
},
"navigation": { "navigation": {
"navigate_to": "Navigate to {panel}",
"logs": "[%key:ui::panel::config::logs::caption%]", "logs": "[%key:ui::panel::config::logs::caption%]",
"automation": "[%key:ui::panel::config::automation::caption%]", "automation": "[%key:ui::panel::config::automation::caption%]",
"script": "[%key:ui::panel::config::script::caption%]", "script": "[%key:ui::panel::config::script::caption%]",
@ -1107,37 +1111,37 @@
"reloading": { "reloading": {
"heading": "YAML configuration reloading", "heading": "YAML configuration reloading",
"introduction": "Some parts of Home Assistant can reload without requiring a restart. Hitting reload will unload their current YAML configuration and load the new one.", "introduction": "Some parts of Home Assistant can reload without requiring a restart. Hitting reload will unload their current YAML configuration and load the new one.",
"reload": "Reload {domain}", "reload": "{domain}",
"core": "Reload location & customizations", "core": "Location & customizations",
"group": "Reload groups, group entities, and group notify services", "group": "Groups, group entities, and notify services",
"automation": "Reload automations", "automation": "Automations",
"script": "Reload scripts", "script": "Scripts",
"scene": "Reload scenes", "scene": "Scenes",
"person": "Reload people", "person": "People",
"zone": "Reload zones", "zone": "Zones",
"input_boolean": "Reload input booleans", "input_boolean": "Input booleans",
"input_text": "Reload input texts", "input_text": "Input texts",
"input_number": "Reload input numbers", "input_number": "Input numbers",
"input_datetime": "Reload input date times", "input_datetime": "Input date times",
"input_select": "Reload input selects", "input_select": "Input selects",
"template": "Reload template entities", "template": "Template entities",
"universal": "Reload universal media player entities", "universal": "Universal media player entities",
"rest": "Reload rest entities, and rest notify services", "rest": "Rest entities and notify services",
"command_line": "Reload command line entities", "command_line": "Command line entities",
"filter": "Reload filter entities", "filter": "Filter entities",
"statistics": "Reload statistics entities", "statistics": "Statistics entities",
"generic": "Reload generic IP camera entities", "generic": "Generic IP camera entities",
"generic_thermostat": "Reload generic thermostat entities", "generic_thermostat": "Generic thermostat entities",
"homekit": "Reload HomeKit", "homekit": "HomeKit",
"min_max": "Reload min/max entities", "min_max": "Min/max entities",
"history_stats": "Reload history stats entities", "history_stats": "History stats entities",
"trend": "Reload trend entities", "trend": "Trend entities",
"ping": "Reload ping binary sensor entities", "ping": "Ping binary sensor entities",
"filesize": "Reload file size entities", "filesize": "File size entities",
"telegram": "Reload telegram notify services", "telegram": "Telegram notify services",
"smtp": "Reload SMTP notify services", "smtp": "SMTP notify services",
"mqtt": "Reload manually configured MQTT entities", "mqtt": "Manually configured MQTT entities",
"rpi_gpio": "Reload Raspberry Pi GPIO entities" "rpi_gpio": "Raspberry Pi GPIO entities"
}, },
"server_management": { "server_management": {
"heading": "Server management", "heading": "Server management",
@ -3848,4 +3852,4 @@
} }
} }
} }
} }

View File

@ -80,10 +80,14 @@ describe("fuzzySequentialMatch", () => {
describe("fuzzyFilterSort", () => { describe("fuzzyFilterSort", () => {
const filter = "ticker"; const filter = "ticker";
const item1 = { text: "automation.ticker", altText: "Stocks", score: 0 }; const item1 = {
const item2 = { text: "sensor.ticker", altText: "Stocks up", score: 0 }; filterText: "automation.ticker",
altText: "Stocks",
score: 0,
};
const item2 = { filterText: "sensor.ticker", altText: "Stocks up", score: 0 };
const item3 = { const item3 = {
text: "automation.check_router", filterText: "automation.check_router",
altText: "Timer Check Router", altText: "Timer Check Router",
score: 0, score: 0,
}; };