diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts
index e813c1e7f0..0758d1257a 100644
--- a/src/components/ha-sidebar.ts
+++ b/src/components/ha-sidebar.ts
@@ -5,6 +5,7 @@ import {
css,
PropertyValues,
property,
+ eventOptions,
} from "lit-element";
import "@polymer/app-layout/app-toolbar/app-toolbar";
import "@polymer/paper-icon-button/paper-icon-button";
@@ -28,6 +29,8 @@ import {
} from "../data/persistent_notification";
import computeDomain from "../common/entity/compute_domain";
import { classMap } from "lit-html/directives/class-map";
+// tslint:disable-next-line: no-duplicate-imports
+import { PaperIconItemElement } from "@polymer/paper-item/paper-icon-item";
const SHOW_AFTER_SPACER = ["config", "developer-tools"];
@@ -103,6 +106,10 @@ class HaSidebar extends LitElement {
@property() private _externalConfig?: ExternalConfig;
@property() private _notifications?: PersistentNotification[];
+ private _mouseLeaveTimeout?: number;
+ private _tooltipHideTimeout?: number;
+ private _recentKeydownActiveUntil = 0;
+
protected render() {
const hass = this.hass;
@@ -134,7 +141,14 @@ class HaSidebar extends LitElement {
: ""}
Home Assistant
-
+
${this._renderPanel(
this._defaultPage,
"hass:apps",
@@ -165,7 +179,10 @@ class HaSidebar extends LitElement {
tabindex="-1"
@click=${this._handleExternalAppConfiguration}
>
-
+
${!this.expanded && notificationCount > 0
@@ -216,7 +235,10 @@ class HaSidebar extends LitElement {
aria-role="option"
aria-label=${hass.localize("panel.profile")}
>
-
+
@@ -225,6 +247,7 @@ class HaSidebar extends LitElement {
+
`;
}
@@ -286,6 +309,91 @@ class HaSidebar extends LitElement {
}
}
+ private get _tooltip() {
+ return this.shadowRoot!.querySelector(".tooltip")! as HTMLDivElement;
+ }
+
+ private _itemMouseEnter(ev: MouseEvent) {
+ // On keypresses on the listbox, we're going to ignore mouse enter events
+ // for 100ms so that we ignore it when pressing down arrow scrolls the
+ // sidebar causing the mouse to hover a new icon
+ if (
+ this.expanded ||
+ new Date().getTime() < this._recentKeydownActiveUntil
+ ) {
+ return;
+ }
+ if (this._mouseLeaveTimeout) {
+ clearTimeout(this._mouseLeaveTimeout);
+ this._mouseLeaveTimeout = undefined;
+ }
+ this._showTooltip(ev.currentTarget as PaperIconItemElement);
+ }
+
+ private _itemMouseLeave() {
+ if (this._mouseLeaveTimeout) {
+ clearTimeout(this._mouseLeaveTimeout);
+ }
+ this._mouseLeaveTimeout = window.setTimeout(() => {
+ this._hideTooltip();
+ }, 500);
+ }
+
+ private _listboxFocusIn(ev) {
+ if (this.expanded || ev.target.nodeName !== "A") {
+ return;
+ }
+ this._showTooltip(ev.target.querySelector("paper-icon-item"));
+ }
+
+ private _listboxFocusOut() {
+ this._hideTooltip();
+ }
+
+ @eventOptions({
+ passive: true,
+ })
+ private _listboxScroll() {
+ // On keypresses on the listbox, we're going to ignore scroll events
+ // for 100ms so that if pressing down arrow scrolls the sidebar, the tooltip
+ // will not be hidden.
+ if (new Date().getTime() < this._recentKeydownActiveUntil) {
+ return;
+ }
+ this._hideTooltip();
+ }
+
+ private _listboxKeydown() {
+ this._recentKeydownActiveUntil = new Date().getTime() + 100;
+ }
+
+ private _showTooltip(item: PaperIconItemElement) {
+ if (this._tooltipHideTimeout) {
+ clearTimeout(this._tooltipHideTimeout);
+ this._tooltipHideTimeout = undefined;
+ }
+ const tooltip = this._tooltip;
+ const listbox = this.shadowRoot!.querySelector("paper-listbox")!;
+ let top = item.offsetTop + 7;
+ if (listbox.contains(item)) {
+ top -= listbox.scrollTop;
+ }
+ tooltip.innerHTML = item.querySelector(".item-text")!.innerHTML;
+ tooltip.style.display = "block";
+ tooltip.style.top = `${top}px`;
+ tooltip.style.left = `${item.offsetLeft + item.clientWidth + 12}px`;
+ }
+
+ private _hideTooltip() {
+ // Delay it a little in case other events are pending processing.
+ if (!this._tooltipHideTimeout) {
+ this._tooltipHideTimeout = window.setTimeout(() => {
+ this._tooltipHideTimeout = undefined;
+ this._tooltip.style.display = "none";
+ }, 10);
+ }
+ }
+
private _handleShowNotificationDrawer() {
fireEvent(this, "hass-show-notifications");
}
@@ -309,7 +417,10 @@ class HaSidebar extends LitElement {
data-panel="${urlPath}"
tabindex="-1"
>
-
+
${title}
@@ -327,10 +438,7 @@ class HaSidebar extends LitElement {
-webkit-user-select: none;
-moz-user-select: none;
border-right: 1px solid var(--divider-color);
- background-color: var(
- --sidebar-background-color,
- var(--primary-background-color)
- );
+ background-color: var(--sidebar-background-color);
width: 64px;
}
:host([expanded]) {
@@ -524,6 +632,18 @@ class HaSidebar extends LitElement {
.dev-tools a {
color: var(--sidebar-icon-color);
}
+
+ .tooltip {
+ display: none;
+ position: absolute;
+ opacity: 0.9;
+ border-radius: 2px;
+ white-space: nowrap;
+ color: var(--sidebar-background-color);
+ background-color: var(--sidebar-text-color);
+ padding: 4px;
+ font-weight: 500;
+ }
`;
}
}