mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 11:46:42 +00:00
Sidebar iteration (#3392)
* Remove hover effect * Reuse render panel func * Show notification badge on the right in expanded mode
This commit is contained in:
parent
a7077dbcb4
commit
3d0c994b9a
@ -31,8 +31,6 @@ import { classMap } from "lit-html/directives/class-map";
|
|||||||
|
|
||||||
const SHOW_AFTER_SPACER = ["config", "developer-tools"];
|
const SHOW_AFTER_SPACER = ["config", "developer-tools"];
|
||||||
|
|
||||||
const computeUrl = (urlPath) => `/${urlPath}`;
|
|
||||||
|
|
||||||
const SUPPORT_SCROLL_IF_NEEDED = "scrollIntoViewIfNeeded" in document.body;
|
const SUPPORT_SCROLL_IF_NEEDED = "scrollIntoViewIfNeeded" in document.body;
|
||||||
|
|
||||||
const SORT_VALUE = {
|
const SORT_VALUE = {
|
||||||
@ -91,22 +89,6 @@ const computePanels = (hass: HomeAssistant): [PanelInfo[], PanelInfo[]] => {
|
|||||||
return [beforeSpacer, afterSpacer];
|
return [beforeSpacer, afterSpacer];
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderPanel = (hass, panel) => html`
|
|
||||||
<a
|
|
||||||
aria-role="option"
|
|
||||||
href="${computeUrl(panel.url_path)}"
|
|
||||||
data-panel="${panel.url_path}"
|
|
||||||
tabindex="-1"
|
|
||||||
>
|
|
||||||
<paper-icon-item>
|
|
||||||
<ha-icon slot="item-icon" .icon="${panel.icon}"></ha-icon>
|
|
||||||
<span class="item-text">
|
|
||||||
${hass.localize(`panel.${panel.title}`) || panel.title}
|
|
||||||
</span>
|
|
||||||
</paper-icon-item>
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
*/
|
*/
|
||||||
@ -116,13 +98,10 @@ class HaSidebar extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public alwaysExpand = false;
|
@property({ type: Boolean }) public alwaysExpand = false;
|
||||||
@property({ type: Boolean, reflect: true }) public expanded = false;
|
@property({ type: Boolean, reflect: true }) public expanded = false;
|
||||||
@property({ type: Boolean, reflect: true }) public expandedWidth = false;
|
|
||||||
@property() public _defaultPage?: string =
|
@property() public _defaultPage?: string =
|
||||||
localStorage.defaultPage || DEFAULT_PANEL;
|
localStorage.defaultPage || DEFAULT_PANEL;
|
||||||
@property() private _externalConfig?: ExternalConfig;
|
@property() private _externalConfig?: ExternalConfig;
|
||||||
@property() private _notifications?: PersistentNotification[];
|
@property() private _notifications?: PersistentNotification[];
|
||||||
private _expandTimeout?: number;
|
|
||||||
private _contractTimeout?: number;
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const hass = this.hass;
|
const hass = this.hass;
|
||||||
@ -156,22 +135,27 @@ class HaSidebar extends LitElement {
|
|||||||
<span class="title">Home Assistant</span>
|
<span class="title">Home Assistant</span>
|
||||||
</div>
|
</div>
|
||||||
<paper-listbox attr-for-selected="data-panel" .selected=${hass.panelUrl}>
|
<paper-listbox attr-for-selected="data-panel" .selected=${hass.panelUrl}>
|
||||||
<a
|
${this._renderPanel(
|
||||||
aria-role="option"
|
this._defaultPage,
|
||||||
href="${computeUrl(this._defaultPage)}"
|
"hass:apps",
|
||||||
data-panel=${this._defaultPage}
|
hass.localize("panel.states")
|
||||||
tabindex="-1"
|
)}
|
||||||
>
|
${beforeSpacer.map((panel) =>
|
||||||
<paper-icon-item>
|
this._renderPanel(
|
||||||
<ha-icon slot="item-icon" icon="hass:apps"></ha-icon>
|
panel.url_path,
|
||||||
<span class="item-text">${hass.localize("panel.states")}</span>
|
panel.icon,
|
||||||
</paper-icon-item>
|
hass.localize(`panel.${panel.title}`) || panel.title
|
||||||
</a>
|
)
|
||||||
|
)}
|
||||||
${beforeSpacer.map((panel) => renderPanel(hass, panel))}
|
|
||||||
<div class="spacer" disabled></div>
|
<div class="spacer" disabled></div>
|
||||||
|
|
||||||
${afterSpacer.map((panel) => renderPanel(hass, panel))}
|
${afterSpacer.map((panel) =>
|
||||||
|
this._renderPanel(
|
||||||
|
panel.url_path,
|
||||||
|
panel.icon,
|
||||||
|
hass.localize(`panel.${panel.title}`) || panel.title
|
||||||
|
)
|
||||||
|
)}
|
||||||
${this._externalConfig && this._externalConfig.hasSettingsScreen
|
${this._externalConfig && this._externalConfig.hasSettingsScreen
|
||||||
? html`
|
? html`
|
||||||
<a
|
<a
|
||||||
@ -203,7 +187,7 @@ class HaSidebar extends LitElement {
|
|||||||
@click=${this._handleShowNotificationDrawer}
|
@click=${this._handleShowNotificationDrawer}
|
||||||
>
|
>
|
||||||
<ha-icon slot="item-icon" icon="hass:bell"></ha-icon>
|
<ha-icon slot="item-icon" icon="hass:bell"></ha-icon>
|
||||||
${notificationCount > 0
|
${!this.expanded && notificationCount > 0
|
||||||
? html`
|
? html`
|
||||||
<span class="notification-badge" slot="item-icon">
|
<span class="notification-badge" slot="item-icon">
|
||||||
${notificationCount}
|
${notificationCount}
|
||||||
@ -213,6 +197,11 @@ class HaSidebar extends LitElement {
|
|||||||
<span class="item-text">
|
<span class="item-text">
|
||||||
${hass.localize("ui.notification_drawer.title")}
|
${hass.localize("ui.notification_drawer.title")}
|
||||||
</span>
|
</span>
|
||||||
|
${this.expanded
|
||||||
|
? html`
|
||||||
|
<span class="notification-badge">${notificationCount}</span>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
</paper-icon-item>
|
</paper-icon-item>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
@ -242,7 +231,6 @@ class HaSidebar extends LitElement {
|
|||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
if (
|
if (
|
||||||
changedProps.has("expanded") ||
|
changedProps.has("expanded") ||
|
||||||
changedProps.has("expandedWidth") ||
|
|
||||||
changedProps.has("narrow") ||
|
changedProps.has("narrow") ||
|
||||||
changedProps.has("alwaysExpand") ||
|
changedProps.has("alwaysExpand") ||
|
||||||
changedProps.has("_externalConfig") ||
|
changedProps.has("_externalConfig") ||
|
||||||
@ -275,21 +263,6 @@ class HaSidebar extends LitElement {
|
|||||||
this._externalConfig = conf;
|
this._externalConfig = conf;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// On tablets, there is no hover. So we receive click and mouseenter at the
|
|
||||||
// same time. In that case, we're going to cancel expanding, because it is
|
|
||||||
// going to require another tap outside the sidebar to trigger mouseleave
|
|
||||||
this.addEventListener("click", () => {
|
|
||||||
if (this._expandTimeout) {
|
|
||||||
clearTimeout(this._expandTimeout);
|
|
||||||
this._expandTimeout = undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.addEventListener("mouseenter", () => {
|
|
||||||
this._expand();
|
|
||||||
});
|
|
||||||
this.addEventListener("mouseleave", () => {
|
|
||||||
this._contract();
|
|
||||||
});
|
|
||||||
subscribeNotifications(this.hass.connection, (notifications) => {
|
subscribeNotifications(this.hass.connection, (notifications) => {
|
||||||
this._notifications = notifications;
|
this._notifications = notifications;
|
||||||
});
|
});
|
||||||
@ -297,9 +270,8 @@ class HaSidebar extends LitElement {
|
|||||||
|
|
||||||
protected updated(changedProps) {
|
protected updated(changedProps) {
|
||||||
super.updated(changedProps);
|
super.updated(changedProps);
|
||||||
if (changedProps.has("alwaysExpand") && this.alwaysExpand) {
|
if (changedProps.has("alwaysExpand")) {
|
||||||
this.expanded = true;
|
this.expanded = this.alwaysExpand;
|
||||||
this.expandedWidth = true;
|
|
||||||
}
|
}
|
||||||
if (!SUPPORT_SCROLL_IF_NEEDED || !changedProps.has("hass")) {
|
if (!SUPPORT_SCROLL_IF_NEEDED || !changedProps.has("hass")) {
|
||||||
return;
|
return;
|
||||||
@ -314,33 +286,6 @@ class HaSidebar extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _expand() {
|
|
||||||
// We debounce it one frame, because on tablets, the mouse-enter and
|
|
||||||
// click event fire at the same time.
|
|
||||||
this._expandTimeout = window.setTimeout(() => {
|
|
||||||
this.expanded = true;
|
|
||||||
this.expandedWidth = true;
|
|
||||||
}, 0);
|
|
||||||
if (this._contractTimeout) {
|
|
||||||
clearTimeout(this._contractTimeout);
|
|
||||||
this._contractTimeout = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _contract() {
|
|
||||||
if (this._expandTimeout) {
|
|
||||||
clearTimeout(this._expandTimeout);
|
|
||||||
this._expandTimeout = undefined;
|
|
||||||
}
|
|
||||||
if (this.alwaysExpand) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.expandedWidth = false;
|
|
||||||
this._contractTimeout = window.setTimeout(() => {
|
|
||||||
this.expanded = this.alwaysExpand || false;
|
|
||||||
}, 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleShowNotificationDrawer() {
|
private _handleShowNotificationDrawer() {
|
||||||
fireEvent(this, "hass-show-notifications");
|
fireEvent(this, "hass-show-notifications");
|
||||||
}
|
}
|
||||||
@ -356,6 +301,22 @@ class HaSidebar extends LitElement {
|
|||||||
fireEvent(this, "hass-toggle-menu");
|
fireEvent(this, "hass-toggle-menu");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _renderPanel(urlPath, icon, title) {
|
||||||
|
return html`
|
||||||
|
<a
|
||||||
|
aria-role="option"
|
||||||
|
href="${`/${urlPath}`}"
|
||||||
|
data-panel="${urlPath}"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<paper-icon-item>
|
||||||
|
<ha-icon slot="item-icon" .icon="${icon}"></ha-icon>
|
||||||
|
<span class="item-text">${title}</span>
|
||||||
|
</paper-icon-item>
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
@ -371,12 +332,8 @@ class HaSidebar extends LitElement {
|
|||||||
var(--primary-background-color)
|
var(--primary-background-color)
|
||||||
);
|
);
|
||||||
width: 64px;
|
width: 64px;
|
||||||
transition: width 0.2s ease-in;
|
|
||||||
will-change: width;
|
|
||||||
contain: strict;
|
|
||||||
transition-delay: 0.2s;
|
|
||||||
}
|
}
|
||||||
:host([expandedwidth]) {
|
:host([expanded]) {
|
||||||
width: 256px;
|
width: 256px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,6 +469,9 @@ class HaSidebar extends LitElement {
|
|||||||
.notifications {
|
.notifications {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.notifications .item-text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
.profile {
|
.profile {
|
||||||
}
|
}
|
||||||
.profile paper-icon-item {
|
.profile paper-icon-item {
|
||||||
@ -522,19 +482,22 @@ class HaSidebar extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.notification-badge {
|
.notification-badge {
|
||||||
position: absolute;
|
min-width: 20px;
|
||||||
font-weight: 400;
|
box-sizing: border-box;
|
||||||
bottom: 14px;
|
|
||||||
left: 26px;
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
font-weight: 400;
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
height: 20px;
|
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 0px 6px;
|
padding: 0px 6px;
|
||||||
font-size: 0.65em;
|
|
||||||
color: var(--text-primary-color);
|
color: var(--text-primary-color);
|
||||||
}
|
}
|
||||||
|
ha-icon + .notification-badge {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 14px;
|
||||||
|
left: 26px;
|
||||||
|
font-size: 0.65em;
|
||||||
|
}
|
||||||
|
|
||||||
.spacer {
|
.spacer {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user