From 2a57ffa615b0b46ddbdb386b5d55e0318feaca74 Mon Sep 17 00:00:00 2001 From: Donnie Date: Wed, 11 Nov 2020 02:46:53 -0800 Subject: [PATCH] Add navigation commands to quick bar commands (#7380) Co-authored-by: Bram Kragten --- src/common/config/can_show_page.ts | 18 ++ src/data/panel.ts | 35 +++- src/dialogs/quick-bar/ha-quick-bar.ts | 163 +++++++++++++++--- .../config/dashboard/ha-config-navigation.ts | 7 +- src/translations/en.json | 22 +++ 5 files changed, 205 insertions(+), 40 deletions(-) create mode 100644 src/common/config/can_show_page.ts diff --git a/src/common/config/can_show_page.ts b/src/common/config/can_show_page.ts new file mode 100644 index 0000000000..0ac4f6d2ec --- /dev/null +++ b/src/common/config/can_show_page.ts @@ -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); diff --git a/src/data/panel.ts b/src/data/panel.ts index eb205c65b0..b71721c71c 100644 --- a/src/data/panel.ts +++ b/src/data/panel.ts @@ -21,6 +21,18 @@ export const getDefaultPanel = (hass: HomeAssistant): PanelInfo => ? hass.panels[hass.defaultPanel] : 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 => { if (!hass.panels) { return undefined; @@ -34,13 +46,20 @@ export const getPanelTitle = (hass: HomeAssistant): string | undefined => { return undefined; } - if (panel.url_path === "lovelace") { - return hass.localize("panel.states"); - } + const translationKey = getPanelNameTranslationKey(panel); - if (panel.url_path === "profile") { - return hass.localize("panel.profile"); - } - - return hass.localize(`panel.${panel.title}`) || panel.title || undefined; + return hass.localize(translationKey) || panel.title || undefined; +}; + +export const getPanelIcon = (panel: PanelInfo): string | null => { + if (!panel.icon) { + switch (panel.component_name) { + case "profile": + return "hass:account"; + case "lovelace": + return "hass:view-dashboard"; + } + } + + return panel.icon; }; diff --git a/src/dialogs/quick-bar/ha-quick-bar.ts b/src/dialogs/quick-bar/ha-quick-bar.ts index 281fd79916..49920892c2 100644 --- a/src/dialogs/quick-bar/ha-quick-bar.ts +++ b/src/dialogs/quick-bar/ha-quick-bar.ts @@ -41,19 +41,36 @@ import { showConfirmationDialog, } from "../generic/show-dialog-box"; 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 { - icon: string; + icon?: string; + iconPath?: string; action(data?: any): void; } +interface QuickBarNavigationItem extends QuickBarItem { + path: string; +} + +interface NavigationInfo extends PageNavigation { + text: string; +} + @customElement("ha-quick-bar") export class QuickBar extends LitElement { @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[] = []; @@ -73,8 +90,7 @@ export class QuickBar extends LitElement { public async showDialog(params: QuickBarParams) { this._commandMode = params.commandMode || this._toggleIfAlreadyOpened(); - this._commandItems = this._generateCommandItems(); - this._entityItems = this._generateEntityItems(); + this._initializeItemsIfNeeded(); 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() { this._setFilteredItems(); this.updateComplete.then(() => { @@ -182,10 +206,15 @@ export class QuickBar extends LitElement { .twoline=${Boolean(item.altText)} .item=${item} index=${ifDefined(index)} - graphic="avatar" + graphic="icon" > - - ${item.text} + ${item.iconPath + ? html`` + : html``} + ${item.text} ${item.altText ? html` ({ + 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[] { return [ ...this._generateReloadCommands(), ...this._generateServerControlCommands(), + ...this._generateNavigationCommands(), ].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}` ) ), - icon: "hass:server", + icon: DEFAULT_SERVER_ICON, action: () => this.hass.callService("homeassistant", action), }, 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( item: QuickBarItem, confirmText: ConfirmationDialogParams["confirmText"] @@ -338,15 +454,13 @@ export class QuickBar extends LitElement { }; } - private _generateEntityItems(): QuickBarItem[] { - return Object.keys(this.hass.states) - .map((entityId) => ({ - text: computeStateName(this.hass.states[entityId]) || 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 _withNavigationActions(items) { + return items.map(({ text, icon, iconPath, path }) => ({ + text, + icon, + iconPath, + action: () => navigate(this, path), + })); } private _toggleIfAlreadyOpened() { @@ -388,16 +502,14 @@ export class QuickBar extends LitElement { } } - ha-icon { - margin-left: 8px; - } - + ha-icon, ha-svg-icon { - color: var(--primary-text-color); + margin-left: 20px; } ha-svg-icon.prefix { margin: 8px; + color: var(--primary-text-color); } .uni-virtualizer-host { @@ -414,10 +526,7 @@ export class QuickBar extends LitElement { mwc-list-item { width: 100%; - } - - .item-text { - margin-left: 16px; + text-transform: capitalize; } `, ]; diff --git a/src/panels/config/dashboard/ha-config-navigation.ts b/src/panels/config/dashboard/ha-config-navigation.ts index b348ada115..e6bff978c8 100644 --- a/src/panels/config/dashboard/ha-config-navigation.ts +++ b/src/panels/config/dashboard/ha-config-navigation.ts @@ -9,7 +9,7 @@ import { property, TemplateResult, } 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-icon-next"; import { CloudStatus, CloudStatusLoggedIn } from "../../../data/cloud"; @@ -27,10 +27,7 @@ class HaConfigNavigation extends LitElement { protected render(): TemplateResult { return html` ${this.pages.map((page) => - (!page.component || - page.core || - isComponentLoaded(this.hass, page.component)) && - (!page.advancedOnly || this.showAdvanced) + canShowPage(this.hass, page) ? html`