mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-03 14:37:47 +00:00
Change commands to use category labels instead of icons. Fixes several translation issues.
This commit is contained in:
parent
b9f802939c
commit
75e5d66966
@ -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)
|
||||||
|
@ -51,24 +51,38 @@ const DEFAULT_NAVIGATION_ICON = "hass:arrow-right-circle";
|
|||||||
const DEFAULT_SERVER_ICON = "hass:server";
|
const DEFAULT_SERVER_ICON = "hass:server";
|
||||||
|
|
||||||
interface QuickBarItem extends ScorableTextItem {
|
interface QuickBarItem extends ScorableTextItem {
|
||||||
|
primaryText: string;
|
||||||
icon?: string;
|
icon?: 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCommandItem = (
|
||||||
|
item: QuickBarItem | 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?: QuickBarItem[];
|
||||||
|
|
||||||
@ -201,6 +215,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: QuickBarItem, index?: number) {
|
||||||
return html`
|
return html`
|
||||||
<mwc-list-item
|
<mwc-list-item
|
||||||
.twoline=${Boolean(item.altText)}
|
.twoline=${Boolean(item.altText)}
|
||||||
@ -214,7 +234,30 @@ export class QuickBar extends LitElement {
|
|||||||
slot="graphic"
|
slot="graphic"
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: html`<ha-icon .icon=${item.icon} slot="graphic"></ha-icon>`}
|
: html`<ha-icon .icon=${item.icon} slot="graphic"></ha-icon>`}
|
||||||
${item.text}
|
<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 class="command-category ${item.categoryKey}"
|
||||||
|
>${item.categoryText}
|
||||||
|
</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"
|
||||||
@ -313,92 +356,110 @@ 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]);
|
||||||
|
return {
|
||||||
|
primaryText,
|
||||||
|
filterText: primaryText,
|
||||||
altText: entityId,
|
altText: entityId,
|
||||||
icon: domainIcon(computeDomain(entityId), this.hass.states[entityId]),
|
icon: domainIcon(computeDomain(entityId), this.hass.states[entityId]),
|
||||||
action: () => fireEvent(this, "hass-more-info", { entityId }),
|
action: () => fireEvent(this, "hass-more-info", { entityId }),
|
||||||
}))
|
};
|
||||||
.sort((a, b) => compare(a.text.toLowerCase(), b.text.toLowerCase()));
|
})
|
||||||
|
.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)
|
||||||
),
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
primaryText,
|
||||||
|
filterText: `${categoryText} ${primaryText}`,
|
||||||
icon: domainIcon(domain),
|
icon: domainIcon(domain),
|
||||||
action: () => this.hass.callService(domain, "reload"),
|
action: () => this.hass.callService(domain, "reload"),
|
||||||
}));
|
categoryKey: "reload",
|
||||||
|
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(
|
||||||
text: this.hass.localize(
|
`ui.dialogs.quick-bar.commands.types.${categoryKey}`
|
||||||
|
);
|
||||||
|
const primaryText = this.hass.localize(
|
||||||
"ui.dialogs.quick-bar.commands.server_control.perform_action",
|
"ui.dialogs.quick-bar.commands.server_control.perform_action",
|
||||||
"action",
|
"action",
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
`ui.dialogs.quick-bar.commands.server_control.${action}`
|
`ui.dialogs.quick-bar.commands.server_control.${action}`
|
||||||
)
|
)
|
||||||
),
|
);
|
||||||
|
|
||||||
|
return this._generateConfirmationCommand(
|
||||||
|
{
|
||||||
|
primaryText,
|
||||||
|
filterText: `${categoryText} ${primaryText}`,
|
||||||
|
categoryKey,
|
||||||
|
categoryText,
|
||||||
icon: DEFAULT_SERVER_ICON,
|
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).map((panelKey) => {
|
return Object.keys(this.hass.panels).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)) {
|
||||||
@ -422,28 +483,20 @@ 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) {
|
return { ...page, primaryText: caption };
|
||||||
const caption = this.hass.localize(
|
|
||||||
"ui.dialogs.quick-bar.commands.navigation.navigate_to_config",
|
|
||||||
"panel",
|
|
||||||
shortCaption
|
|
||||||
);
|
|
||||||
|
|
||||||
return { ...page, text: caption };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _generateConfirmationCommand(
|
private _generateConfirmationCommand(
|
||||||
item: QuickBarItem,
|
item: CommandItem,
|
||||||
confirmText: ConfirmationDialogParams["confirmText"]
|
confirmText: ConfirmationDialogParams["confirmText"]
|
||||||
): QuickBarItem {
|
): CommandItem {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
action: () =>
|
action: () =>
|
||||||
@ -454,13 +507,23 @@ 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,
|
||||||
|
categoryText,
|
||||||
|
filterText: `${categoryText} ${item.primaryText}`,
|
||||||
|
action: () => navigate(this, item.path),
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleIfAlreadyOpened() {
|
private _toggleIfAlreadyOpened() {
|
||||||
@ -512,6 +575,27 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.command-category.reload {
|
||||||
|
background: pink;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.command-category.navigation {
|
||||||
|
background: lightblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.command-category.server_control {
|
||||||
|
background: orange;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.uni-virtualizer-host {
|
.uni-virtualizer-host {
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -526,7 +610,6 @@ export class QuickBar extends LitElement {
|
|||||||
|
|
||||||
mwc-list-item {
|
mwc-list-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@ -288,7 +288,6 @@
|
|||||||
"overflow_menu": "Overflow menu",
|
"overflow_menu": "Overflow menu",
|
||||||
"successfully_saved": "Successfully saved",
|
"successfully_saved": "Successfully saved",
|
||||||
"successfully_deleted": "Successfully deleted",
|
"successfully_deleted": "Successfully deleted",
|
||||||
|
|
||||||
"error_required": "Required",
|
"error_required": "Required",
|
||||||
"copied": "Copied"
|
"copied": "Copied"
|
||||||
},
|
},
|
||||||
@ -496,13 +495,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": "Go to",
|
||||||
|
"server_control": "Server"
|
||||||
|
},
|
||||||
"navigation": {
|
"navigation": {
|
||||||
"navigate_to": "Navigate to {panel}",
|
|
||||||
"navigate_to_config": "Navigate to {panel} configuration",
|
|
||||||
"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%]",
|
||||||
@ -1026,36 +1028,36 @@
|
|||||||
"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": "Reload {domain}",
|
||||||
"core": "Reload location & customizations",
|
"core": "Location & customizations",
|
||||||
"group": "Reload groups, group entities, and 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 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",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user