mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-17 14:26:35 +00:00
Add navigation commands to quick bar commands (#7380)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
216fce74f8
commit
2a57ffa615
18
src/common/config/can_show_page.ts
Normal file
18
src/common/config/can_show_page.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { isComponentLoaded } from "./is_component_loaded";
|
||||||
|
import { PageNavigation } from "../../layouts/hass-tabs-subpage";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
|
export const canShowPage = (hass: HomeAssistant, page: PageNavigation) => {
|
||||||
|
return (
|
||||||
|
(isCore(page) || isLoadedIntegration(hass, page)) &&
|
||||||
|
!hideAdvancedPage(hass, page)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) =>
|
||||||
|
!page.component || isComponentLoaded(hass, page.component);
|
||||||
|
const isCore = (page: PageNavigation) => page.core;
|
||||||
|
const isAdvancedPage = (page: PageNavigation) => page.advancedOnly;
|
||||||
|
const userWantsAdvanced = (hass: HomeAssistant) => hass.userData?.showAdvanced;
|
||||||
|
const hideAdvancedPage = (hass: HomeAssistant, page: PageNavigation) =>
|
||||||
|
isAdvancedPage(page) && !userWantsAdvanced(hass);
|
@ -21,6 +21,18 @@ export const getDefaultPanel = (hass: HomeAssistant): PanelInfo =>
|
|||||||
? hass.panels[hass.defaultPanel]
|
? hass.panels[hass.defaultPanel]
|
||||||
: hass.panels[DEFAULT_PANEL];
|
: hass.panels[DEFAULT_PANEL];
|
||||||
|
|
||||||
|
export const getPanelNameTranslationKey = (panel: PanelInfo): string => {
|
||||||
|
if (panel.url_path === "lovelace") {
|
||||||
|
return "panel.states";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (panel.url_path === "profile") {
|
||||||
|
return "panel.profile";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `panel.${panel.title}`;
|
||||||
|
};
|
||||||
|
|
||||||
export const getPanelTitle = (hass: HomeAssistant): string | undefined => {
|
export const getPanelTitle = (hass: HomeAssistant): string | undefined => {
|
||||||
if (!hass.panels) {
|
if (!hass.panels) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -34,13 +46,20 @@ export const getPanelTitle = (hass: HomeAssistant): string | undefined => {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (panel.url_path === "lovelace") {
|
const translationKey = getPanelNameTranslationKey(panel);
|
||||||
return hass.localize("panel.states");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (panel.url_path === "profile") {
|
return hass.localize(translationKey) || panel.title || undefined;
|
||||||
return hass.localize("panel.profile");
|
};
|
||||||
}
|
|
||||||
|
export const getPanelIcon = (panel: PanelInfo): string | null => {
|
||||||
return hass.localize(`panel.${panel.title}`) || panel.title || undefined;
|
if (!panel.icon) {
|
||||||
|
switch (panel.component_name) {
|
||||||
|
case "profile":
|
||||||
|
return "hass:account";
|
||||||
|
case "lovelace":
|
||||||
|
return "hass:view-dashboard";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return panel.icon;
|
||||||
};
|
};
|
||||||
|
@ -41,19 +41,36 @@ 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 { navigate } from "../../common/navigate";
|
||||||
|
import { configSections } from "../../panels/config/ha-panel-config";
|
||||||
|
import { PageNavigation } from "../../layouts/hass-tabs-subpage";
|
||||||
|
import { canShowPage } from "../../common/config/can_show_page";
|
||||||
|
import { getPanelIcon, getPanelNameTranslationKey } from "../../data/panel";
|
||||||
|
|
||||||
|
const DEFAULT_NAVIGATION_ICON = "hass:arrow-right-circle";
|
||||||
|
const DEFAULT_SERVER_ICON = "hass:server";
|
||||||
|
|
||||||
interface QuickBarItem extends ScorableTextItem {
|
interface QuickBarItem extends ScorableTextItem {
|
||||||
icon: string;
|
icon?: string;
|
||||||
|
iconPath?: string;
|
||||||
action(data?: any): void;
|
action(data?: any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface QuickBarNavigationItem extends QuickBarItem {
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NavigationInfo extends PageNavigation {
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
@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?: QuickBarItem[];
|
||||||
|
|
||||||
@internalProperty() private _entityItems: QuickBarItem[] = [];
|
@internalProperty() private _entityItems?: QuickBarItem[];
|
||||||
|
|
||||||
@internalProperty() private _items?: QuickBarItem[] = [];
|
@internalProperty() private _items?: QuickBarItem[] = [];
|
||||||
|
|
||||||
@ -73,8 +90,7 @@ export class QuickBar extends LitElement {
|
|||||||
|
|
||||||
public async showDialog(params: QuickBarParams) {
|
public async showDialog(params: QuickBarParams) {
|
||||||
this._commandMode = params.commandMode || this._toggleIfAlreadyOpened();
|
this._commandMode = params.commandMode || this._toggleIfAlreadyOpened();
|
||||||
this._commandItems = this._generateCommandItems();
|
this._initializeItemsIfNeeded();
|
||||||
this._entityItems = this._generateEntityItems();
|
|
||||||
this._opened = true;
|
this._opened = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,6 +174,14 @@ export class QuickBar extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _initializeItemsIfNeeded() {
|
||||||
|
if (this._commandMode) {
|
||||||
|
this._commandItems = this._commandItems || this._generateCommandItems();
|
||||||
|
} else {
|
||||||
|
this._entityItems = this._entityItems || this._generateEntityItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _handleOpened() {
|
private _handleOpened() {
|
||||||
this._setFilteredItems();
|
this._setFilteredItems();
|
||||||
this.updateComplete.then(() => {
|
this.updateComplete.then(() => {
|
||||||
@ -182,10 +206,15 @@ export class QuickBar extends LitElement {
|
|||||||
.twoline=${Boolean(item.altText)}
|
.twoline=${Boolean(item.altText)}
|
||||||
.item=${item}
|
.item=${item}
|
||||||
index=${ifDefined(index)}
|
index=${ifDefined(index)}
|
||||||
graphic="avatar"
|
graphic="icon"
|
||||||
>
|
>
|
||||||
<ha-icon .icon=${item.icon} slot="graphic"></ha-icon>
|
${item.iconPath
|
||||||
<span class="item-text">${item.text}</span>
|
? html`<ha-svg-icon
|
||||||
|
.path=${item.iconPath}
|
||||||
|
slot="graphic"
|
||||||
|
></ha-svg-icon>`
|
||||||
|
: html`<ha-icon .icon=${item.icon} slot="graphic"></ha-icon>`}
|
||||||
|
${item.text}
|
||||||
${item.altText
|
${item.altText
|
||||||
? html`
|
? html`
|
||||||
<span slot="secondary" class="item-text secondary"
|
<span slot="secondary" class="item-text secondary"
|
||||||
@ -253,6 +282,8 @@ export class QuickBar extends LitElement {
|
|||||||
if (oldCommandMode !== this._commandMode) {
|
if (oldCommandMode !== this._commandMode) {
|
||||||
this._items = undefined;
|
this._items = undefined;
|
||||||
this._focusSet = false;
|
this._focusSet = false;
|
||||||
|
|
||||||
|
this._initializeItemsIfNeeded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,10 +311,22 @@ export class QuickBar extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 }),
|
||||||
|
}))
|
||||||
|
.sort((a, b) => compare(a.text.toLowerCase(), b.text.toLowerCase()));
|
||||||
|
}
|
||||||
|
|
||||||
private _generateCommandItems(): QuickBarItem[] {
|
private _generateCommandItems(): QuickBarItem[] {
|
||||||
return [
|
return [
|
||||||
...this._generateReloadCommands(),
|
...this._generateReloadCommands(),
|
||||||
...this._generateServerControlCommands(),
|
...this._generateServerControlCommands(),
|
||||||
|
...this._generateNavigationCommands(),
|
||||||
].sort((a, b) => compare(a.text.toLowerCase(), b.text.toLowerCase()));
|
].sort((a, b) => compare(a.text.toLowerCase(), b.text.toLowerCase()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,7 +359,7 @@ export class QuickBar extends LitElement {
|
|||||||
`ui.dialogs.quick-bar.commands.server_control.${action}`
|
`ui.dialogs.quick-bar.commands.server_control.${action}`
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
icon: "hass:server",
|
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")
|
||||||
@ -324,6 +367,79 @@ export class QuickBar extends LitElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _generateNavigationCommands(): QuickBarItem[] {
|
||||||
|
const panelItems = this._generateNavigationPanelCommands();
|
||||||
|
const sectionItems = this._generateNavigationConfigSectionCommands();
|
||||||
|
|
||||||
|
return this._withNavigationActions([...panelItems, ...sectionItems]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _generateNavigationPanelCommands(): Omit<
|
||||||
|
QuickBarNavigationItem,
|
||||||
|
"action"
|
||||||
|
>[] {
|
||||||
|
return Object.keys(this.hass.panels).map((panelKey) => {
|
||||||
|
const panel = this.hass.panels[panelKey];
|
||||||
|
const translationKey = getPanelNameTranslationKey(panel);
|
||||||
|
|
||||||
|
const text = this.hass.localize(
|
||||||
|
"ui.dialogs.quick-bar.commands.navigation.navigate_to",
|
||||||
|
"panel",
|
||||||
|
this.hass.localize(translationKey) || panel.title || panel.url_path
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
text,
|
||||||
|
icon: getPanelIcon(panel) || DEFAULT_NAVIGATION_ICON,
|
||||||
|
path: `/${panel.url_path}`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _generateNavigationConfigSectionCommands(): Partial<
|
||||||
|
QuickBarNavigationItem
|
||||||
|
>[] {
|
||||||
|
const items: NavigationInfo[] = [];
|
||||||
|
|
||||||
|
for (const sectionKey of Object.keys(configSections)) {
|
||||||
|
for (const page of configSections[sectionKey]) {
|
||||||
|
if (canShowPage(this.hass, page)) {
|
||||||
|
if (page.component) {
|
||||||
|
const info = this._getNavigationInfoFromConfig(page);
|
||||||
|
|
||||||
|
if (info) {
|
||||||
|
items.push(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getNavigationInfoFromConfig(
|
||||||
|
page: PageNavigation
|
||||||
|
): NavigationInfo | undefined {
|
||||||
|
if (page.component) {
|
||||||
|
const shortCaption = this.hass.localize(
|
||||||
|
`ui.dialogs.quick-bar.commands.navigation.${page.component}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (page.translationKey) {
|
||||||
|
const caption = this.hass.localize(
|
||||||
|
"ui.dialogs.quick-bar.commands.navigation.navigate_to_config",
|
||||||
|
"panel",
|
||||||
|
shortCaption
|
||||||
|
);
|
||||||
|
|
||||||
|
return { ...page, text: caption };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
private _generateConfirmationCommand(
|
private _generateConfirmationCommand(
|
||||||
item: QuickBarItem,
|
item: QuickBarItem,
|
||||||
confirmText: ConfirmationDialogParams["confirmText"]
|
confirmText: ConfirmationDialogParams["confirmText"]
|
||||||
@ -338,15 +454,13 @@ export class QuickBar extends LitElement {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _generateEntityItems(): QuickBarItem[] {
|
private _withNavigationActions(items) {
|
||||||
return Object.keys(this.hass.states)
|
return items.map(({ text, icon, iconPath, path }) => ({
|
||||||
.map((entityId) => ({
|
text,
|
||||||
text: computeStateName(this.hass.states[entityId]) || entityId,
|
icon,
|
||||||
altText: entityId,
|
iconPath,
|
||||||
icon: domainIcon(computeDomain(entityId), this.hass.states[entityId]),
|
action: () => navigate(this, path),
|
||||||
action: () => fireEvent(this, "hass-more-info", { entityId }),
|
}));
|
||||||
}))
|
|
||||||
.sort((a, b) => compare(a.text.toLowerCase(), b.text.toLowerCase()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleIfAlreadyOpened() {
|
private _toggleIfAlreadyOpened() {
|
||||||
@ -388,16 +502,14 @@ export class QuickBar extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-icon {
|
ha-icon,
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-svg-icon {
|
ha-svg-icon {
|
||||||
color: var(--primary-text-color);
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-svg-icon.prefix {
|
ha-svg-icon.prefix {
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-virtualizer-host {
|
.uni-virtualizer-host {
|
||||||
@ -414,10 +526,7 @@ export class QuickBar extends LitElement {
|
|||||||
|
|
||||||
mwc-list-item {
|
mwc-list-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
text-transform: capitalize;
|
||||||
|
|
||||||
.item-text {
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { canShowPage } from "../../../common/config/can_show_page";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon-next";
|
import "../../../components/ha-icon-next";
|
||||||
import { CloudStatus, CloudStatusLoggedIn } from "../../../data/cloud";
|
import { CloudStatus, CloudStatusLoggedIn } from "../../../data/cloud";
|
||||||
@ -27,10 +27,7 @@ class HaConfigNavigation extends LitElement {
|
|||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
${this.pages.map((page) =>
|
${this.pages.map((page) =>
|
||||||
(!page.component ||
|
canShowPage(this.hass, page)
|
||||||
page.core ||
|
|
||||||
isComponentLoaded(this.hass, page.component)) &&
|
|
||||||
(!page.advancedOnly || this.showAdvanced)
|
|
||||||
? html`
|
? html`
|
||||||
<a
|
<a
|
||||||
href=${`/config/${page.component}`}
|
href=${`/config/${page.component}`}
|
||||||
|
@ -499,6 +499,28 @@
|
|||||||
"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%]"
|
||||||
|
},
|
||||||
|
"navigation": {
|
||||||
|
"navigate_to": "Navigate to {panel}",
|
||||||
|
"navigate_to_config": "Navigate to {panel} configuration",
|
||||||
|
"logs": "[%key:ui::panel::config::logs::caption%]",
|
||||||
|
"automation": "[%key:ui::panel::config::automation::caption%]",
|
||||||
|
"script": "[%key:ui::panel::config::script::caption%]",
|
||||||
|
"integrations": "[%key:ui::panel::config::integrations::caption%]",
|
||||||
|
"areas": "[%key:ui::panel::config::areas::caption%]",
|
||||||
|
"scene": "[%key:ui::panel::config::scene::caption%]",
|
||||||
|
"helpers": "[%key:ui::panel::config::helpers::caption%]",
|
||||||
|
"tags": "[%key:ui::panel::config::tags::caption%]",
|
||||||
|
"person": "[%key:ui::panel::config::person::caption%]",
|
||||||
|
"devices": "[%key:ui::panel::config::devices::caption%]",
|
||||||
|
"entities": "[%key:ui::panel::config::entities::caption%]",
|
||||||
|
"lovelace": "[%key:ui::panel::config::lovelace::caption%]",
|
||||||
|
"core": "[%key:ui::panel::config::core::caption%]",
|
||||||
|
"zone": "[%key:ui::panel::config::zone::caption%]",
|
||||||
|
"users": "[%key:ui::panel::config::users::caption%]",
|
||||||
|
"info": "[%key:ui::panel::config::info::caption%]",
|
||||||
|
"customize": "[%key:ui::panel::config::customize::caption%]",
|
||||||
|
"server_control": "[%key:ui::panel::config::server_control::caption%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"filter_placeholder": "Entity Filter"
|
"filter_placeholder": "Entity Filter"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user