Show a notification dot on toggle menu button in narrow mode (#3323)

* Show a notification dot on toggle menu button in narrow mode

* Fix lint

* Move menu button to sidebar

* Fix height sidebar
This commit is contained in:
Paulus Schoutsen 2019-06-30 15:02:53 -07:00 committed by GitHub
parent 0a7cb39500
commit 203b14613f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 246 additions and 127 deletions

View File

@ -36,6 +36,7 @@ customElements.get("paper-icon-button").prototype._keyBindings = {};
class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
@property() public hass!: HomeAssistant;
@property() public panel!: HassioPanelInfo;
@property() public narrow!: boolean;
protected routerOptions: RouterOptions = {
// Hass.io has a page with tabs, so we route all non-matching routes to it.
@ -108,6 +109,7 @@ class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
// As long as we have Polymer pages
(el as PolymerElement).setProperties({
hass: this.hass,
narrow: this.narrow,
supervisorInfo: this._supervisorInfo,
hostInfo: this._hostInfo,
hassInfo: this._hassInfo,
@ -115,6 +117,7 @@ class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
});
} else {
el.hass = this.hass;
el.narrow = this.narrow;
el.supervisorInfo = this._supervisorInfo;
el.hostInfo = this._hostInfo;
el.hassInfo = this._hassInfo;

View File

@ -34,6 +34,7 @@ const HAS_REFRESH_BUTTON = ["store", "snapshots"];
@customElement("hassio-pages-with-tabs")
class HassioPagesWithTabs extends LitElement {
@property() public hass!: HomeAssistant;
@property() public narrow!: boolean;
@property() public route!: Route;
@property() public supervisorInfo!: HassioSupervisorInfo;
@property() public hostInfo!: HassioHostInfo;
@ -45,7 +46,11 @@ class HassioPagesWithTabs extends LitElement {
<app-header-layout has-scrolling-region>
<app-header fixed slot="header">
<app-toolbar>
<ha-menu-button hassio></ha-menu-button>
<ha-menu-button
.hass=${this.hass}
.narrow=${this.narrow}
hassio
></ha-menu-button>
<div main-title>Hass.io</div>
${HAS_REFRESH_BUTTON.includes(page)
? html`

View File

@ -5,34 +5,113 @@ import {
LitElement,
html,
customElement,
CSSResult,
css,
} from "lit-element";
import { fireEvent } from "../common/dom/fire_event";
import { HomeAssistant } from "../types";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { subscribeNotifications } from "../data/persistent_notification";
import computeDomain from "../common/entity/compute_domain";
@customElement("ha-menu-button")
class HaMenuButton extends LitElement {
@property({ type: Boolean })
public hassio = false;
@property({ type: Boolean }) public hassio = false;
@property() public narrow!: boolean;
@property() public hass!: HomeAssistant;
@property() private _hasNotifications = false;
private _attachNotifOnConnect = false;
private _unsubNotifications?: UnsubscribeFunc;
public connectedCallback() {
super.connectedCallback();
if (this._attachNotifOnConnect) {
this._attachNotifOnConnect = false;
this._subscribeNotifications();
}
}
public disconnectedCallback() {
super.disconnectedCallback();
if (this._unsubNotifications) {
this._attachNotifOnConnect = true;
this._unsubNotifications();
this._unsubNotifications = undefined;
}
}
protected render(): TemplateResult | void {
const hasNotifications =
this.narrow &&
(this._hasNotifications ||
Object.keys(this.hass.states).some(
(entityId) => computeDomain(entityId) === "configurator"
));
return html`
<paper-icon-button
aria-label="Sidebar Toggle"
.icon=${this.hassio ? "hassio:menu" : "hass:menu"}
@click=${this._toggleMenu}
></paper-icon-button>
${hasNotifications
? html`
<div class="dot"></div>
`
: ""}
`;
}
// We are not going to use ShadowDOM as we're rendering a single element
// without any CSS used.
protected createRenderRoot(): Element | ShadowRoot {
return this;
protected updated(changedProps) {
super.updated(changedProps);
if (!changedProps.has("narrow")) {
return;
}
this.style.visibility = this.narrow ? "initial" : "hidden";
if (!this.narrow) {
this._hasNotifications = false;
if (this._unsubNotifications) {
this._unsubNotifications();
this._unsubNotifications = undefined;
}
return;
}
this._subscribeNotifications();
}
private _subscribeNotifications() {
this._unsubNotifications = subscribeNotifications(
this.hass.connection,
(notifications) => {
this._hasNotifications = notifications.length > 0;
}
);
}
private _toggleMenu(): void {
fireEvent(this, "hass-toggle-menu");
}
static get styles(): CSSResult {
return css`
:host {
position: relative;
}
.dot {
position: absolute;
background-color: var(--accent-color);
width: 12px;
height: 12px;
top: 8px;
right: 5px;
border-radius: 50%;
}
`;
}
}
declare global {

View File

@ -14,6 +14,7 @@ import "@polymer/paper-listbox/paper-listbox";
import "./ha-icon";
import "../components/user/ha-user-badge";
import "../components/ha-menu-button";
import { HomeAssistant, PanelInfo } from "../types";
import { fireEvent } from "../common/dom/fire_event";
import { DEFAULT_PANEL } from "../common/const";
@ -108,6 +109,7 @@ const renderPanel = (hass, panel) => html`
*/
class HaSidebar extends LitElement {
@property() public hass!: HomeAssistant;
@property() public narrow!: boolean;
@property({ type: Boolean }) public alwaysExpand = false;
@property({ type: Boolean, reflect: true }) public expanded = false;
@ -135,21 +137,18 @@ class HaSidebar extends LitElement {
}
return html`
${this.expanded
? html`
<app-toolbar>
<div main-title>Home Assistant</div>
</app-toolbar>
`
: html`
<div class="logo">
<img
id="logo"
src="/static/icons/favicon-192x192.png"
alt="Home Assistant logo"
/>
</div>
`}
<div class="menu">
${!this.narrow
? html`
<paper-icon-button
aria-label="Sidebar Toggle"
.icon=${hass.dockedSidebar ? "hass:menu-open" : "hass:menu"}
@click=${this._toggleSidebar}
></paper-icon-button>
`
: ""}
<span class="title">Home Assistant</span>
</div>
<paper-listbox attr-for-selected="data-panel" .selected=${hass.panelUrl}>
<a
@ -234,6 +233,7 @@ class HaSidebar extends LitElement {
protected shouldUpdate(changedProps: PropertyValues): boolean {
if (
changedProps.has("expanded") ||
changedProps.has("narrow") ||
changedProps.has("alwaysExpand") ||
changedProps.has("_externalConfig") ||
changedProps.has("_notifications")
@ -264,26 +264,15 @@ class HaSidebar extends LitElement {
this._externalConfig = conf;
});
}
this.shadowRoot!.querySelector("paper-listbox")!.addEventListener(
"mouseenter",
() => {
this.expanded = true;
}
);
this.addEventListener("mouseenter", () => {
this.expanded = true;
});
this.addEventListener("mouseleave", () => {
this._contract();
});
subscribeNotifications(this.hass.connection, (notifications) => {
this._notifications = notifications;
});
// Deal with configurator
// private _updateNotifications(
// states: HassEntities,
// persistent: unknown[]
// ): unknown[] {
// const configurator = computeNotifications(states);
// return persistent.concat(configurator);
// }
}
protected updated(changedProps) {
@ -308,6 +297,10 @@ class HaSidebar extends LitElement {
});
}
private _toggleSidebar() {
fireEvent(this, "hass-toggle-menu");
}
static get styles(): CSSResult {
return css`
:host {
@ -332,26 +325,35 @@ class HaSidebar extends LitElement {
width: 256px;
}
.logo {
height: 65px;
box-sizing: border-box;
padding: 8px;
.menu {
height: 64px;
display: flex;
padding: 0 12px;
border-bottom: 1px solid transparent;
}
.logo img {
width: 48px;
}
app-toolbar {
white-space: nowrap;
font-weight: 400;
color: var(--primary-text-color);
border-bottom: 1px solid var(--divider-color);
background-color: var(--primary-background-color);
font-size: 20px;
align-items: center;
}
:host([expanded]) .menu {
width: 256px;
}
app-toolbar a {
color: var(--primary-text-color);
.menu paper-icon-button {
color: var(--sidebar-icon-color);
}
:host([expanded]) .menu paper-icon-button {
margin-right: 23px;
}
.title {
display: none;
}
:host([expanded]) .title {
display: initial;
}
paper-listbox {

View File

@ -12,17 +12,23 @@ import {
import "../components/ha-menu-button";
import "../components/ha-paper-icon-button-arrow-prev";
import { haStyle } from "../resources/styles";
import { HomeAssistant } from "../types";
@customElement("hass-loading-screen")
class HassLoadingScreen extends LitElement {
@property({ type: Boolean }) public rootnav? = false;
@property() public hass?: HomeAssistant;
@property() public narrow?: boolean;
protected render(): TemplateResult | void {
return html`
<app-toolbar>
${this.rootnav
? html`
<ha-menu-button></ha-menu-button>
<ha-menu-button
.hass=${this.hass}
.narrow=${this.narrow}
></ha-menu-button>
`
: html`
<ha-paper-icon-button-arrow-prev

View File

@ -15,25 +15,16 @@ class HassSubpage extends LitElement {
@property()
public header?: string;
@property({ type: Boolean })
public root = false;
@property({ type: Boolean })
public hassio = false;
protected render(): TemplateResult | void {
return html`
<div class="toolbar">
${this.root
? html`
<ha-menu-button .hassio=${this.hassio}></ha-menu-button>
`
: html`
<ha-paper-icon-button-arrow-prev
.hassio=${this.hassio}
@click=${this._backTapped}
></ha-paper-icon-button-arrow-prev>
`}
<ha-paper-icon-button-arrow-prev
.hassio=${this.hassio}
@click=${this._backTapped}
></ha-paper-icon-button-arrow-prev>
<div main-title>${this.header}</div>
<slot name="toolbar-icon"></slot>

View File

@ -86,6 +86,8 @@ class PartialPanelResolver extends HassRouterPage {
protected createLoadingScreen() {
const el = super.createLoadingScreen();
el.rootnav = true;
el.hass = this.hass;
el.narrow = this.narrow;
return el;
}

View File

@ -67,7 +67,10 @@ class HaPanelCalendar extends LocalizeMixin(PolymerElement) {
<app-header-layout has-scrolling-region>
<app-header slot="header" fixed>
<app-toolbar>
<ha-menu-button></ha-menu-button>
<ha-menu-button
hass="[[hass]]"
narrow="[[narrow]]"
></ha-menu-button>
<div main-title>[[localize('panel.calendar')]]</div>
</app-toolbar>
</app-header>

View File

@ -47,7 +47,7 @@ class HaConfigDashboard extends NavigateMixin(LocalizeMixin(PolymerElement)) {
<app-header-layout has-scrolling-region="">
<app-header slot="header" fixed="">
<app-toolbar>
<ha-menu-button></ha-menu-button>
<ha-menu-button hass='[[hass]]' narrow='[[narrow]]'></ha-menu-button>
<div main-title="">[[localize('panel.config')]]</div>
</app-toolbar>
</app-header>
@ -125,6 +125,7 @@ class HaConfigDashboard extends NavigateMixin(LocalizeMixin(PolymerElement)) {
static get properties() {
return {
hass: Object,
narrow: Boolean,
isWide: Boolean,
cloudStatus: Object,
showAdvanced: Boolean,

View File

@ -9,6 +9,7 @@ import {
CoreFrontendUserData,
getOptimisticFrontendUserDataCollection,
} from "../../data/frontend";
import { PolymerElement } from "@polymer/polymer";
declare global {
// for fire event
@ -142,12 +143,29 @@ class HaPanelConfig extends HassRouterPage {
}
protected updatePageEl(el) {
el.route = this.routeTail;
el.hass = this.hass;
el.showAdvanced = !!(this._coreUserData && this._coreUserData.showAdvanced);
el.isWide = this.hass.dockedSidebar ? this._wideSidebar : this._wide;
el.narrow = this.narrow;
el.cloudStatus = this._cloudStatus;
const showAdvanced = !!(
this._coreUserData && this._coreUserData.showAdvanced
);
const isWide = this.hass.dockedSidebar ? this._wideSidebar : this._wide;
if ("setProperties" in el) {
// As long as we have Polymer panels
(el as PolymerElement).setProperties({
route: this.routeTail,
hass: this.hass,
showAdvanced,
isWide,
narrow: this.narrow,
cloudStatus: this._cloudStatus,
});
} else {
el.route = this.routeTail;
el.hass = this.hass;
el.showAdvanced = showAdvanced;
el.isWide = isWide;
el.narrow = this.narrow;
el.cloudStatus = this._cloudStatus;
}
}
private async _updateCloudStatus() {

View File

@ -37,7 +37,10 @@ class PanelDeveloperTools extends LitElement {
<app-header-layout has-scrolling-region>
<app-header fixed slot="header">
<app-toolbar>
<ha-menu-button></ha-menu-button>
<ha-menu-button
.hass=${this.hass}
.narrow=${this.narrow}
></ha-menu-button>
<div main-title>Developer Tools</div>
</app-toolbar>
<paper-tabs

View File

@ -1,6 +1,3 @@
import "@polymer/app-layout/app-header-layout/app-header-layout";
import "@polymer/app-layout/app-header/app-header";
import "@polymer/app-layout/app-toolbar/app-toolbar";
import "@material/mwc-button";
import "@polymer/paper-input/paper-input";
import "@polymer/paper-input/paper-textarea";
@ -8,7 +5,6 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../components/ha-card";
import "../../../components/ha-menu-button";
import "../../../resources/ha-style";
import "../../../util/app-localstorage-document";
@ -34,39 +30,28 @@ class HaPanelDevMqtt extends PolymerElement {
}
</style>
<app-header-layout has-scrolling-region>
<app-header slot="header" fixed>
<app-toolbar>
<ha-menu-button></ha-menu-button>
<div main-title>MQTT</div>
</app-toolbar>
</app-header>
<app-localstorage-document key="panel-dev-mqtt-topic" data="{{topic}}">
</app-localstorage-document>
<app-localstorage-document
key="panel-dev-mqtt-payload"
data="{{payload}}"
>
</app-localstorage-document>
<app-localstorage-document key="panel-dev-mqtt-topic" data="{{topic}}">
</app-localstorage-document>
<app-localstorage-document
key="panel-dev-mqtt-payload"
data="{{payload}}"
>
</app-localstorage-document>
<ha-card header="Publish a packet">
<div class="card-content">
<paper-input label="topic" value="{{topic}}"></paper-input>
<div class="content">
<ha-card header="Publish a packet">
<div class="card-content">
<paper-input label="topic" value="{{topic}}"></paper-input>
<paper-textarea
always-float-label
label="Payload (template allowed)"
value="{{payload}}"
></paper-textarea>
</div>
<div class="card-actions">
<mwc-button on-click="_publish">Publish</mwc-button>
</div>
</ha-card>
<paper-textarea
always-float-label
label="Payload (template allowed)"
value="{{payload}}"
></paper-textarea>
</div>
</app-header-layout>
<div class="card-actions">
<mwc-button on-click="_publish">Publish</mwc-button>
</div>
</ha-card>
`;
}

View File

@ -65,7 +65,10 @@ class HaPanelHistory extends LocalizeMixin(PolymerElement) {
<app-header-layout has-scrolling-region>
<app-header slot="header" fixed>
<app-toolbar>
<ha-menu-button></ha-menu-button>
<ha-menu-button
hass="[[hass]]"
narrow="[[narrow]]"
></ha-menu-button>
<div main-title>[[localize('panel.history')]]</div>
</app-toolbar>
</app-header>
@ -116,9 +119,8 @@ class HaPanelHistory extends LocalizeMixin(PolymerElement) {
static get properties() {
return {
hass: {
type: Object,
},
hass: Object,
narrow: Boolean,
stateHistory: {
type: Object,

View File

@ -16,7 +16,7 @@ class HaPanelIframe extends PolymerElement {
}
</style>
<app-toolbar>
<ha-menu-button></ha-menu-button>
<ha-menu-button hass="[[hass]]" narrow="[[narrow]]"></ha-menu-button>
<div main-title>[[panel.title]]</div>
</app-toolbar>
@ -32,9 +32,9 @@ class HaPanelIframe extends PolymerElement {
static get properties() {
return {
panel: {
type: Object,
},
hass: Object,
narrow: Boolean,
panel: Object,
};
}
}

View File

@ -89,7 +89,10 @@ class HaPanelLogbook extends LocalizeMixin(PolymerElement) {
<app-header-layout has-scrolling-region>
<app-header slot="header" fixed>
<app-toolbar>
<ha-menu-button></ha-menu-button>
<ha-menu-button
hass="[[hass]]"
narrow="[[narrow]]"
></ha-menu-button>
<div main-title>[[localize('panel.logbook')]]</div>
<paper-icon-button
icon="hass:refresh"
@ -157,9 +160,8 @@ class HaPanelLogbook extends LocalizeMixin(PolymerElement) {
static get properties() {
return {
hass: {
type: Object,
},
hass: Object,
narrow: Boolean,
// ISO8601 formatted date string
_currentDate: {

View File

@ -95,7 +95,11 @@ class LovelacePanel extends LitElement {
}
return html`
<hass-loading-screen rootnav></hass-loading-screen>
<hass-loading-screen
rootnav
.hass=${this.hass}
.narrow=${this.narrow}
></hass-loading-screen>
`;
}

View File

@ -131,7 +131,10 @@ class HUIRoot extends LitElement {
`
: html`
<app-toolbar>
<ha-menu-button></ha-menu-button>
<ha-menu-button
.hass=${this.hass}
.narrow=${this.narrow}
></ha-menu-button>
<div main-title>${this.config.title || "Home Assistant"}</div>
<ha-start-voice-button
.hass="${this.hass}"

View File

@ -80,7 +80,10 @@ class HaPanelMailbox extends EventsMixin(LocalizeMixin(PolymerElement)) {
<app-header-layout has-scrolling-region>
<app-header slot="header" fixed>
<app-toolbar>
<ha-menu-button></ha-menu-button>
<ha-menu-button
hass="[[hass]]"
narrow="[[narrow]]"
></ha-menu-button>
<div main-title>[[localize('panel.mailbox')]]</div>
</app-toolbar>
<div sticky hidden$="[[areTabsHidden(platforms)]]">
@ -128,9 +131,8 @@ class HaPanelMailbox extends EventsMixin(LocalizeMixin(PolymerElement)) {
static get properties() {
return {
hass: {
type: Object,
},
hass: Object,
narrow: Boolean,
platforms: {
type: Array,

View File

@ -27,7 +27,7 @@ class HaPanelMap extends LocalizeMixin(PolymerElement) {
</style>
<app-toolbar>
<ha-menu-button></ha-menu-button>
<ha-menu-button hass="[[hass]]" narrow="[[narrow]]"></ha-menu-button>
<div main-title>[[localize('panel.map')]]</div>
</app-toolbar>
@ -41,6 +41,7 @@ class HaPanelMap extends LocalizeMixin(PolymerElement) {
type: Object,
observer: "drawEntities",
},
narrow: Boolean,
};
}

View File

@ -55,7 +55,7 @@ class HaPanelProfile extends EventsMixin(LocalizeMixin(PolymerElement)) {
<app-header-layout has-scrolling-region>
<app-header slot="header" fixed>
<app-toolbar>
<ha-menu-button></ha-menu-button>
<ha-menu-button hass='[[hass]]' narrow='[[narrow]]'></ha-menu-button>
<div main-title>[[localize('panel.profile')]]</div>
</app-toolbar>
</app-header>

View File

@ -67,7 +67,10 @@ class HaPanelShoppingList extends LocalizeMixin(PolymerElement) {
<app-header-layout has-scrolling-region>
<app-header slot="header" fixed>
<app-toolbar>
<ha-menu-button></ha-menu-button>
<ha-menu-button
hass="[[hass]]"
narrow="[[narrow]]"
></ha-menu-button>
<div main-title>[[localize('panel.shopping_list')]]</div>
<ha-start-voice-button
hass="[[hass]]"
@ -139,6 +142,7 @@ class HaPanelShoppingList extends LocalizeMixin(PolymerElement) {
static get properties() {
return {
hass: Object,
narrow: Boolean,
canListen: Boolean,
items: {
type: Array,

View File

@ -65,7 +65,10 @@ class PartialCards extends EventsMixin(NavigateMixin(PolymerElement)) {
<ha-app-layout id="layout">
<app-header effects="waterfall" condenses="" fixed="" slot="header">
<app-toolbar>
<ha-menu-button></ha-menu-button>
<ha-menu-button
hass="[[hass]]"
narrow="[[narrow]]"
></ha-menu-button>
<div main-title="">
[[computeTitle(views, defaultView, locationName)]]
</div>