From 75c7445dd995b1d961eeca39b36a8accc961c885 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 20 Jul 2019 10:31:13 -0700 Subject: [PATCH] Show a tooltip when hovering sidebar items in compact mode (#3393) * Show a tooltip when hovering sidebar items in compact mode * Use div for tooltip --- src/components/ha-sidebar.ts | 136 ++++++++++++++++++++++++++++++++--- 1 file changed, 128 insertions(+), 8 deletions(-) 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; + } `; } }