Refactor sidebar to use mwc-list

This commit is contained in:
Donnie 2020-10-24 19:29:34 -07:00
parent 8f6ec03446
commit 24f334bfe2
5 changed files with 495 additions and 116 deletions

View File

@ -0,0 +1,52 @@
import "./ha-clickable-ripple";
import { ListItem } from "@material/mwc-list/mwc-list-item";
// import { ListItemBase } from "@material/mwc-list/mwc-list-item-base";
import { css, CSSResult, customElement } from "lit-element";
import { html } from "lit-html";
@customElement("ha-clickable-list-item")
export class HaClickableListItem extends ListItem {
public href?: string;
public render() {
const r = super.render();
const href = this.href ? `/${this.href}` : "";
return html` ${this.renderRipple()}
<a aria-role="option" href=${href}>
${r}
</a>`;
}
static get styles(): CSSResult[] {
return [
super.styles,
css`
:host {
padding-left: 0px;
padding-right: 0px;
}
:host([graphic="avatar"]:not([twoLine])),
:host([graphic="icon"]:not([twoLine])) {
height: 48px;
}
a {
width: 100%;
height: 100%;
display: flex;
align-items: center;
padding-left: var(--mdc-list-side-padding, 16px);
padding-right: var(--mdc-list-side-padding, 16px);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-clickable-list-item": HaClickableListItem;
}
}

View File

@ -0,0 +1,70 @@
import { ListItem } from "@material/mwc-list/mwc-list-item";
import { Ripple } from "@material/mwc-ripple";
import { RippleBase } from "@material/mwc-ripple/mwc-ripple-base";
// import { ListItemBase } from "@material/mwc-list/mwc-list-item-base";
import { css, CSSResult, customElement } from "lit-element";
import { html } from "lit-html";
@customElement("ha-clickable-ripple")
export class HaClickableRipple extends RippleBase {
public href?: string;
// public render() {
// const r = super.render();
// // prettier-ignore
// return html`
// <a
// aria-role="option"
// href=${`/${this.href}`}
// >
// ${r}
// </a>`;
// }
public render() {
/** @classMap */
const classes = {
"mdc-ripple-upgraded--unbounded": this.unbounded,
"mdc-ripple-upgraded--background-focused": this.bgFocused,
"mdc-ripple-upgraded--foreground-activation": super.fgActivation,
"mdc-ripple-upgraded--foreground-deactivation": super.fgDeactivation,
hover: super.hovering,
primary: this.primary,
accent: this.accent,
disabled: this.disabled,
activated: this.activated,
selected: this.selected,
};
return html` <div
class="mdc-ripple-surface mdc-ripple-upgraded ${classMap(classes)}"
style="${styleMap({
"--mdc-ripple-fg-scale": this.fgScale,
"--mdc-ripple-fg-size": this.fgSize,
"--mdc-ripple-fg-translate-end": this.translateEnd,
"--mdc-ripple-fg-translate-start": this.translateStart,
"--mdc-ripple-left": this.leftPos,
"--mdc-ripple-top": this.topPos,
})}"
></div>`;
}
static get styles(): CSSResult[] {
return [
super.styles,
css`
/* a {
width: 100%;
height: 100%;
border: 1px solid black;
vertical-align: middle;
} */
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-clickable-ripple": HaClickableRipple;
}
}

View File

@ -1,3 +1,6 @@
import "./ha-clickable-list-item";
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-list/mwc-list";
import "@material/mwc-button/mwc-button"; import "@material/mwc-button/mwc-button";
import "@material/mwc-icon-button"; import "@material/mwc-icon-button";
import { import {
@ -9,10 +12,6 @@ import {
mdiPlus, mdiPlus,
mdiViewDashboard, mdiViewDashboard,
} from "@mdi/js"; } from "@mdi/js";
import "@polymer/paper-item/paper-icon-item";
import type { PaperIconItemElement } from "@polymer/paper-item/paper-icon-item";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import { import {
css, css,
CSSResult, CSSResult,
@ -210,10 +209,10 @@ class HaSidebar extends LitElement {
return html` return html`
${this._renderHeader()} ${this._renderHeader()}
${this._renderAllPanels()} ${this._renderAllPanels()}
${this._renderDivider()} ${this._renderUtilityPanels()}
${this._renderNotifications()} ${this._renderNotifications()}
${this._renderUserItem()} ${this._renderUserItem()}
<div disabled class="bottom-spacer"></div> ${this._renderSpacer()}
<div class="tooltip"></div> <div class="tooltip"></div>
`; `;
} }
@ -338,10 +337,9 @@ class HaSidebar extends LitElement {
// prettier-ignore // prettier-ignore
return html` return html`
<paper-listbox <mwc-list
attr-for-selected="data-panel" attr-for-selected="data-panel"
class="ha-scrollbar" class="ha-scrollbar"
.selected=${this.hass.panelUrl}
@focusin=${this._listboxFocusIn} @focusin=${this._listboxFocusIn}
@focusout=${this._listboxFocusOut} @focusout=${this._listboxFocusOut}
@scroll=${this._listboxScroll} @scroll=${this._listboxScroll}
@ -353,7 +351,7 @@ class HaSidebar extends LitElement {
${this._renderSpacer()} ${this._renderSpacer()}
${this._renderPanels(afterSpacer)} ${this._renderPanels(afterSpacer)}
${this._renderExternalConfiguration()} ${this._renderExternalConfiguration()}
</paper-listbox> </mwc-list>
`; `;
} }
@ -375,13 +373,14 @@ class HaSidebar extends LitElement {
if (!panel) { if (!panel) {
return ""; return "";
} }
return html`<paper-icon-item return html`<ha-clickable-list-item
@click=${this._unhidePanel} @click=${this._unhidePanel}
class="hidden-panel" class="hidden-panel"
.panel=${url} .panel=${url}
graphic="icon"
> >
<ha-icon <ha-icon
slot="item-icon" slot="graphic"
.icon=${panel.url_path === this.hass.defaultPanel .icon=${panel.url_path === this.hass.defaultPanel
? "mdi:view-dashboard" ? "mdi:view-dashboard"
: panel.icon} : panel.icon}
@ -395,18 +394,14 @@ class HaSidebar extends LitElement {
<mwc-icon-button class="show-panel"> <mwc-icon-button class="show-panel">
<ha-svg-icon .path=${mdiPlus}></ha-svg-icon> <ha-svg-icon .path=${mdiPlus}></ha-svg-icon>
</mwc-icon-button> </mwc-icon-button>
</paper-icon-item>`; </ha-clickable-list-item>`;
})} })}
${this._renderSpacer()}` ${this._renderSpacer()}`
: ""}`; : ""}`;
} }
private _renderDivider() {
return html`<div class="divider"></div>`;
}
private _renderSpacer() { private _renderSpacer() {
return html`<div class="spacer" disabled></div>`; return html`<li divider role="separator" class="spacer"></li>`;
} }
private _renderNotifications() { private _renderNotifications() {
@ -419,20 +414,19 @@ class HaSidebar extends LitElement {
} }
} }
return html` <div return html`
class="notifications-container" <ha-clickable-list-item
@mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave}
>
<paper-icon-item
class="notifications"
aria-role="option" aria-role="option"
@click=${this._handleShowNotificationDrawer} @click=${this._handleShowNotificationDrawer}
graphic="icon"
hasMeta
@mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave}
> >
<ha-svg-icon slot="item-icon" .path=${mdiBell}></ha-svg-icon> <ha-svg-icon slot="graphic" .path=${mdiBell}></ha-svg-icon>
${!this.expanded && notificationCount > 0 ${!this.expanded && notificationCount > 0
? html` ? html`
<span class="notification-badge" slot="item-icon"> <span slot="graphic" class="notification-badge">
${notificationCount} ${notificationCount}
</span> </span>
` `
@ -441,45 +435,48 @@ class HaSidebar extends LitElement {
${this.hass.localize("ui.notification_drawer.title")} ${this.hass.localize("ui.notification_drawer.title")}
</span> </span>
${this.expanded && notificationCount > 0 ${this.expanded && notificationCount > 0
? html` <span class="notification-badge">${notificationCount}</span> ` ? html`
<span class="notification-badge" slot="meta"
>${notificationCount}</span
>
`
: ""} : ""}
</paper-icon-item> </ha-clickable-list-item>
</div>`; `;
} }
private _renderUserItem() { private _renderUserItem() {
return html`<a return html`<ha-clickable-list-item
class=${classMap({ class=${classMap({
profile: true, profile: true,
// Mimick behavior that paper-listbox provides // Mimick behavior that mwc-list provides
"iron-selected": this.hass.panelUrl === "profile", "iron-selected": this.hass.panelUrl === "profile",
})} })}
href="/profile" .href=${"profile"}
data-panel="panel" data-panel="panel"
tabindex="-1" tabindex="-1"
aria-role="option"
aria-label=${this.hass.localize("panel.profile")} aria-label=${this.hass.localize("panel.profile")}
@mouseenter=${this._itemMouseEnter} @mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave} @mouseleave=${this._itemMouseLeave}
graphic="icon"
.activated=${this.hass.panelUrl === "profile"}
> >
<paper-icon-item> <ha-user-badge
<ha-user-badge slot="graphic"
slot="item-icon" .user=${this.hass.user}
.user=${this.hass.user} .hass=${this.hass}
.hass=${this.hass} ></ha-user-badge>
></ha-user-badge>
<span class="item-text"> <span class="item-text">
${this.hass.user ? this.hass.user.name : ""} ${this.hass.user ? this.hass.user.name : ""}
</span> </span>
</paper-icon-item> </ha-clickable-list-item> `;
</a>`;
} }
private _renderExternalConfiguration() { private _renderExternalConfiguration() {
return html`${this._externalConfig && this._externalConfig.hasSettingsScreen return html`${this._externalConfig && this._externalConfig.hasSettingsScreen
? html` ? html`
<a <ha-clickable-list-item
aria-role="option" aria-role="option"
aria-label=${this.hass.localize( aria-label=${this.hass.localize(
"ui.sidebar.external_app_configuration" "ui.sidebar.external_app_configuration"
@ -489,17 +486,13 @@ class HaSidebar extends LitElement {
@click=${this._handleExternalAppConfiguration} @click=${this._handleExternalAppConfiguration}
@mouseenter=${this._itemMouseEnter} @mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave} @mouseleave=${this._itemMouseLeave}
graphic="icon"
> >
<paper-icon-item> <ha-svg-icon slot="graphic" .path=${mdiCellphoneCog}></ha-svg-icon>
<ha-svg-icon <span class="item-text">
slot="item-icon" ${this.hass.localize("ui.sidebar.external_app_configuration")}
.path=${mdiCellphoneCog} </span>
></ha-svg-icon> </ha-clickable-list-item>
<span class="item-text">
${this.hass.localize("ui.sidebar.external_app_configuration")}
</span>
</paper-icon-item>
</a>
` `
: ""}`; : ""}`;
} }
@ -520,7 +513,7 @@ class HaSidebar extends LitElement {
if (!Sortable) { if (!Sortable) {
const [sortableImport, sortStylesImport] = await Promise.all([ const [sortableImport, sortStylesImport] = await Promise.all([
import("sortablejs/modular/sortable.core.esm"), import("sortablejs/modular/sortable.core.esm"),
import("../resources/ha-sortable-style"), import("../resources/ha-sortable-style-ha-clickable"),
]); ]);
const style = document.createElement("style"); const style = document.createElement("style");
@ -541,8 +534,9 @@ class HaSidebar extends LitElement {
this._sortable = new Sortable(this.shadowRoot!.getElementById("sortable"), { this._sortable = new Sortable(this.shadowRoot!.getElementById("sortable"), {
animation: 150, animation: 150,
fallbackClass: "sortable-fallback", fallbackClass: "sortable-fallback",
// fallbackTolerance: 15,
dataIdAttr: "data-panel", dataIdAttr: "data-panel",
handle: "paper-icon-item", handle: "ha-clickable-list-item",
onSort: async () => { onSort: async () => {
this._panelOrder = this._sortable.toArray(); this._panelOrder = this._sortable.toArray();
}, },
@ -604,7 +598,7 @@ class HaSidebar extends LitElement {
clearTimeout(this._mouseLeaveTimeout); clearTimeout(this._mouseLeaveTimeout);
this._mouseLeaveTimeout = undefined; this._mouseLeaveTimeout = undefined;
} }
this._showTooltip(ev.currentTarget as PaperIconItemElement); this._showTooltip(ev.currentTarget);
} }
private _itemMouseLeave() { private _itemMouseLeave() {
@ -620,7 +614,7 @@ class HaSidebar extends LitElement {
if (this.expanded || ev.target.nodeName !== "A") { if (this.expanded || ev.target.nodeName !== "A") {
return; return;
} }
this._showTooltip(ev.target.querySelector("paper-icon-item")); this._showTooltip(ev.target.querySelector("ha-clickable-list-item"));
} }
private _listboxFocusOut() { private _listboxFocusOut() {
@ -644,13 +638,13 @@ class HaSidebar extends LitElement {
this._recentKeydownActiveUntil = new Date().getTime() + 100; this._recentKeydownActiveUntil = new Date().getTime() + 100;
} }
private _showTooltip(item: PaperIconItemElement) { private _showTooltip(item) {
if (this._tooltipHideTimeout) { if (this._tooltipHideTimeout) {
clearTimeout(this._tooltipHideTimeout); clearTimeout(this._tooltipHideTimeout);
this._tooltipHideTimeout = undefined; this._tooltipHideTimeout = undefined;
} }
const tooltip = this._tooltip; const tooltip = this._tooltip;
const listbox = this.shadowRoot!.querySelector("paper-listbox")!; const listbox = this.shadowRoot!.querySelector("mwc-list")!;
let top = item.offsetTop + 11; let top = item.offsetTop + 11;
if (listbox.contains(item)) { if (listbox.contains(item)) {
top -= listbox.scrollTop; top -= listbox.scrollTop;
@ -689,21 +683,77 @@ class HaSidebar extends LitElement {
fireEvent(this, "hass-toggle-menu"); fireEvent(this, "hass-toggle-menu");
} }
private _renderPanels(panels: PanelInfo[]) { private _isUtilityPanel(panel: PanelInfo) {
return panels.map((panel) => return (
this._renderPanel( panel.component_name === "developer-tools" ||
panel.url_path, panel.component_name === "config" ||
panel.url_path === this.hass.defaultPanel panel.component_name === "external-config"
? panel.title || this.hass.localize("panel.states")
: this.hass.localize(`panel.${panel.title}`) || panel.title,
panel.icon,
panel.url_path === this.hass.defaultPanel && !panel.icon
? mdiViewDashboard
: undefined
)
); );
} }
private _renderPanels(panels: PanelInfo[]) {
return panels
.filter((panel) => !this._isUtilityPanel(panel))
.map((panel) =>
this._renderPanel(
panel.url_path,
panel.url_path === this.hass.defaultPanel
? panel.title || this.hass.localize("panel.states")
: this.hass.localize(`panel.${panel.title}`) || panel.title,
panel.icon,
panel.url_path === this.hass.defaultPanel && !panel.icon
? mdiViewDashboard
: undefined
)
);
}
private _renderUtilityPanels() {
const [, afterSpacer] = computePanels(
this.hass.panels,
this.hass.defaultPanel,
this._panelOrder,
this._hiddenPanels
);
// prettier-ignore
return html`
<mwc-list
attr-for-selected="data-panel"
@focusin=${this._listboxFocusIn}
@focusout=${this._listboxFocusOut}
@scroll=${this._listboxScroll}
@keydown=${this._listboxKeydown}
>
${afterSpacer.map((panel) =>
this._renderPanel(
panel.url_path,
panel.url_path === this.hass.defaultPanel
? panel.title || this.hass.localize("panel.states")
: this.hass.localize(`panel.${panel.title}`) || panel.title,
panel.icon,
panel.url_path === this.hass.defaultPanel && !panel.icon
? mdiViewDashboard
: undefined
)
)}
${afterSpacer.map((panel) =>
this._renderPanel(
panel.url_path,
panel.url_path === this.hass.defaultPanel
? panel.title || this.hass.localize("panel.states")
: this.hass.localize(`panel.${panel.title}`) || panel.title,
panel.icon,
panel.url_path === this.hass.defaultPanel && !panel.icon
? mdiViewDashboard
: undefined
)
)}
<li divider role="separator" class="spacer"></li>
</mwc-list>
`;
}
private _renderPanel( private _renderPanel(
urlPath: string, urlPath: string,
title: string | null, title: string | null,
@ -711,23 +761,21 @@ class HaSidebar extends LitElement {
iconPath?: string | null iconPath?: string | null
) { ) {
return html` return html`
<a <ha-clickable-list-item
aria-role="option" .activated=${urlPath === this.hass.panelUrl}
href=${`/${urlPath}`} .href=${urlPath}
data-panel=${urlPath} data-panel=${urlPath}
tabindex="-1" tabindex="-1"
@mouseenter=${this._itemMouseEnter} @mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave} @mouseleave=${this._itemMouseLeave}
graphic="icon"
> >
<paper-icon-item> ${iconPath
${iconPath ? html`<ha-svg-icon slot="graphic" .path=${iconPath}></ha-svg-icon>`
? html`<ha-svg-icon : html`<ha-icon slot="graphic" .icon=${icon}></ha-icon>`}
slot="item-icon"
.path=${iconPath} <span class="item-text">${title}</span>
></ha-svg-icon>`
: html`<ha-icon slot="item-icon" .icon=${icon}></ha-icon>`}
<span class="item-text">${title}</span>
</paper-icon-item>
${this.editMode ${this.editMode
? html`<mwc-icon-button ? html`<mwc-icon-button
class="hide-panel" class="hide-panel"
@ -737,7 +785,7 @@ class HaSidebar extends LitElement {
<ha-svg-icon .path=${mdiClose}></ha-svg-icon> <ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</mwc-icon-button>` </mwc-icon-button>`
: ""} : ""}
</a> </ha-clickable-list-item>
`; `;
} }
@ -806,7 +854,6 @@ class HaSidebar extends LitElement {
} }
:host([narrow]) .title { :host([narrow]) .title {
margin: 0; margin: 0;
padding: 0 16px;
} }
:host([expanded]) .title { :host([expanded]) .title {
display: initial; display: initial;
@ -822,21 +869,22 @@ class HaSidebar extends LitElement {
display: none; display: none;
} }
paper-listbox { mwc-list.ha-scrollbar {
--mdc-list-vertical-padding: 4px 0;
padding: 4px 0; padding: 4px 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-sizing: border-box; box-sizing: border-box;
height: calc(100% - var(--header-height) - 132px); height: calc(100% - var(--header-height) - 350px);
height: calc( height: calc(
100% - var(--header-height) - 132px - env(safe-area-inset-bottom) 100% - var(--header-height) - 350px - env(safe-area-inset-bottom)
); );
overflow-x: hidden; overflow-x: hidden;
background: none; background: none;
margin-left: env(safe-area-inset-left); margin-left: env(safe-area-inset-left);
} }
:host([rtl]) paper-listbox { :host([rtl]) mwc-list {
margin-left: initial; margin-left: initial;
margin-right: env(safe-area-inset-right); margin-right: env(safe-area-inset-right);
} }
@ -847,32 +895,36 @@ class HaSidebar extends LitElement {
font-weight: 500; font-weight: 500;
font-size: 14px; font-size: 14px;
position: relative; position: relative;
display: block;
outline: 0; outline: 0;
border: 1px solid black;
} }
paper-icon-item { mwc-list-item {
box-sizing: border-box; box-sizing: border-box;
margin: 4px; margin: 4px;
padding-left: 12px;
border-radius: 4px; border-radius: 4px;
--paper-item-min-height: 40px;
width: 48px; width: 48px;
--mdc-list-item-graphic-margin: 16px;
--mdc-list-item-meta-size: 32px;
} }
:host([expanded]) paper-icon-item { :host([expanded]) paper-icon-item {
width: 248px; width: 248px;
} }
:host([rtl]) paper-icon-item { :host([rtl]) mwc-list-item {
padding-left: auto; padding-left: auto;
padding-right: 12px; padding-right: 12px;
} }
ha-icon[slot="item-icon"], ha-icon[slot="graphic"],
ha-svg-icon[slot="item-icon"] { ha-svg-icon[slot="graphic"] {
color: var(--sidebar-icon-color); color: var(--sidebar-icon-color);
} }
.iron-selected paper-icon-item::before, [slot="graphic"] {
width: 100%;
}
.iron-selected mwc-list-item::before,
a:not(.iron-selected):focus::before { a:not(.iron-selected):focus::before {
border-radius: 4px; border-radius: 4px;
position: absolute; position: absolute;
@ -885,7 +937,7 @@ class HaSidebar extends LitElement {
transition: opacity 15ms linear; transition: opacity 15ms linear;
will-change: opacity; will-change: opacity;
} }
.iron-selected paper-icon-item::before { .iron-selected mwc-list-item::before {
background-color: var(--sidebar-selected-icon-color); background-color: var(--sidebar-selected-icon-color);
opacity: 0.12; opacity: 0.12;
} }
@ -894,23 +946,24 @@ class HaSidebar extends LitElement {
opacity: var(--dark-divider-opacity); opacity: var(--dark-divider-opacity);
margin: 4px 8px; margin: 4px 8px;
} }
.iron-selected paper-icon-item:focus::before, .iron-selected mwc-list-item:focus::before,
.iron-selected:focus paper-icon-item::before { .iron-selected:focus mwc-list-item::before {
opacity: 0.2; opacity: 0.2;
} }
.iron-selected paper-icon-item[pressed]:before { .iron-selected mwc-list-item[pressed]:before {
opacity: 0.37; opacity: 0.37;
} }
paper-icon-item span { mwc-list-item span {
color: var(--sidebar-text-color); color: var(--sidebar-text-color);
font-weight: 500; font-weight: 500;
font-size: 14px; font-size: 14px;
width: 100%;
} }
a.iron-selected paper-icon-item ha-icon, a.iron-selected mwc-list-item ha-icon,
a.iron-selected paper-icon-item ha-svg-icon { a.iron-selected mwc-list-item ha-svg-icon {
color: var(--sidebar-selected-icon-color); color: var(--sidebar-selected-icon-color);
} }
@ -918,22 +971,21 @@ class HaSidebar extends LitElement {
color: var(--sidebar-selected-text-color); color: var(--sidebar-selected-text-color);
} }
paper-icon-item .item-text { mwc-list-item mwc-list-item .item-text,
mwc-list-item .item-text {
display: none; display: none;
max-width: calc(100% - 56px); max-width: calc(100% - 56px);
} }
:host([expanded]) paper-icon-item .item-text { :host([expanded]) mwc-list-item .item-text {
display: block; display: block;
} }
.divider { .divider {
bottom: 112px;
padding: 10px 0; padding: 10px 0;
} }
.divider::before { .divider::before {
content: " ";
display: block; display: block;
height: 1px; height: 100px;
background-color: var(--divider-color); background-color: var(--divider-color);
} }
.notifications-container { .notifications-container {
@ -957,10 +1009,10 @@ class HaSidebar extends LitElement {
margin-left: initial; margin-left: initial;
margin-right: env(safe-area-inset-right); margin-right: env(safe-area-inset-right);
} }
.profile paper-icon-item { .profile mwc-list-item {
padding-left: 4px; padding-left: 4px;
} }
:host([rtl]) .profile paper-icon-item { :host([rtl]) .profile mwc-list-item {
padding-left: auto; padding-left: auto;
padding-right: 4px; padding-right: 4px;
} }
@ -972,15 +1024,16 @@ class HaSidebar extends LitElement {
} }
.notification-badge { .notification-badge {
min-width: 20px; /* min-width: 20px;
box-sizing: border-box; box-sizing: border-box;
border-radius: 50%; border-radius: 50%;
font-weight: 400; font-weight: 400;
background-color: var(--accent-color); background-color: var(--accent-color);
line-height: 20px; line-height: 30px;
text-align: center; text-align: center;
padding: 0px 6px; padding: 0px 4px;
color: var(--text-accent-color, var(--text-primary-color)); color: var(--text-accent-color, var(--text-primary-color));
font-size: 14px; */
} }
ha-svg-icon + .notification-badge { ha-svg-icon + .notification-badge {
position: absolute; position: absolute;
@ -994,6 +1047,12 @@ class HaSidebar extends LitElement {
pointer-events: none; pointer-events: none;
} }
.bottom-spacer {
flex: 1;
pointer-events: none;
height: 100%;
}
.subheader { .subheader {
color: var(--sidebar-text-color); color: var(--sidebar-text-color);
font-weight: 500; font-weight: 500;

View File

@ -0,0 +1,99 @@
import { css } from "lit-element";
export const sortableStyles = css`
#sortable ha-clickable-list-item:nth-of-type(2n) {
animation-name: keyframes1;
animation-iteration-count: infinite;
transform-origin: 50% 10%;
animation-delay: -0.75s;
animation-duration: 0.25s;
}
#sortable ha-clickable-list-item:nth-of-type(2n-1) {
animation-name: keyframes2;
animation-iteration-count: infinite;
animation-direction: alternate;
transform-origin: 30% 5%;
animation-delay: -0.5s;
animation-duration: 0.33s;
}
#sortable a {
height: 48px;
display: flex;
}
#sortable {
outline: none;
display: block !important;
}
.hidden-panel {
display: flex !important;
}
.sortable-fallback {
display: none;
}
.sortable-ghost {
opacity: 0.4;
}
.sortable-fallback {
opacity: 0;
}
@keyframes keyframes1 {
0% {
transform: rotate(-1deg);
animation-timing-function: ease-in;
}
50% {
transform: rotate(1.5deg);
animation-timing-function: ease-out;
}
}
@keyframes keyframes2 {
0% {
transform: rotate(1deg);
animation-timing-function: ease-in;
}
50% {
transform: rotate(-1.5deg);
animation-timing-function: ease-out;
}
}
.show-panel,
.hide-panel {
display: none;
position: absolute;
top: 0;
right: 0;
--mdc-icon-button-size: 40px;
}
.hide-panel {
top: 4px;
right: 8px;
}
:host([expanded]) .hide-panel {
display: block;
}
:host([expanded]) .show-panel {
display: inline-flex;
}
ha-clickable-list-item.hidden-panel,
ha-clickable-list-item.hidden-panel span,
ha-clickable-list-item.hidden-panel ha-icon[slot="item-icon"] {
color: var(--secondary-text-color);
cursor: pointer;
}
`;

View File

@ -0,0 +1,99 @@
import { css } from "lit-element";
export const sortableStyles = css`
#sortable mwc-list-item:nth-of-type(2n) {
animation-name: keyframes1;
animation-iteration-count: infinite;
transform-origin: 50% 10%;
animation-delay: -0.75s;
animation-duration: 0.25s;
}
#sortable mwc-list-item:nth-of-type(2n-1) {
animation-name: keyframes2;
animation-iteration-count: infinite;
animation-direction: alternate;
transform-origin: 30% 5%;
animation-delay: -0.5s;
animation-duration: 0.33s;
}
#sortable mwc-list-item {
height: 48px;
display: flex;
}
#sortable {
outline: none;
display: block !important;
}
.hidden-panel {
display: flex !important;
}
.sortable-fallback {
display: none;
}
.sortable-ghost {
opacity: 0.4;
}
.sortable-fallback {
opacity: 0;
}
@keyframes keyframes1 {
0% {
transform: rotate(-1deg);
animation-timing-function: ease-in;
}
50% {
transform: rotate(1.5deg);
animation-timing-function: ease-out;
}
}
@keyframes keyframes2 {
0% {
transform: rotate(1deg);
animation-timing-function: ease-in;
}
50% {
transform: rotate(-1.5deg);
animation-timing-function: ease-out;
}
}
.show-panel,
.hide-panel {
display: none;
position: absolute;
top: 0;
right: 0;
--mdc-icon-button-size: 40px;
}
.hide-panel {
top: 4px;
right: 8px;
}
:host([expanded]) .hide-panel {
display: block;
}
:host([expanded]) .show-panel {
display: inline-flex;
}
mwc-list-item.hidden-panel,
mwc-list-item.hidden-panel span,
mwc-list-item.hidden-panel ha-icon[slot="item-icon"] {
color: var(--secondary-text-color);
cursor: pointer;
}
`;