Compare commits

...

16 Commits

Author SHA1 Message Date
Donnie
f138ec8ce0 Update src/components/ha-sidebar.ts
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-04-02 12:28:03 -07:00
Donnie
b6cf935953 Consolidate ha-sortable-style and remove rtl styling 2021-04-02 12:28:02 -07:00
Donnie
dca9bec03c Fix typescript error from trying to override listitem styles into an array 2021-04-02 12:28:02 -07:00
Donnie
aa6915cb90 Fix issue with being unable to drag sidebar items when clicking on text or icon 2021-04-02 12:28:01 -07:00
Donnie
28405f6ddf Allow keyboard nav to cycle to top and bottom of full list. Fix font weight and collapse alignment. 2021-04-02 12:28:01 -07:00
Donnie
366bea4344 Fix down arrow regression 2021-04-02 12:28:00 -07:00
Bram Kragten
173c6bc4c3 Fix 2021-04-02 12:28:00 -07:00
Bram Kragten
91e00815df Some styling tweaks 2021-04-02 12:27:59 -07:00
Donnie
29d9219481 Fix hidden items spacer separation 2021-04-02 12:27:59 -07:00
Donnie
1369d0f36d Fix rtl and remove some unused CSS 2021-04-02 12:27:58 -07:00
Donnie
4a38f7a9e0 Fix padding from left edge to list item graphic 2021-04-02 12:27:58 -07:00
Donnie
12aab855d7 Fix notification badge and user avatar circle 2021-04-02 12:27:57 -07:00
Donnie
f24b1ea844 Remove experimental test code 2021-04-02 12:27:57 -07:00
Donnie
3345b52da4 Fix divider issue, restore arrow down/up behavior, enter key clicks item 2021-04-02 12:27:56 -07:00
Donnie
6ec35380ac Pull divider and panel list out into their own components 2021-04-02 12:27:55 -07:00
Donnie
24f334bfe2 Refactor sidebar to use mwc-list 2021-04-02 12:27:55 -07:00
4 changed files with 456 additions and 236 deletions

View File

@@ -0,0 +1,61 @@
import { style } from "@material/mwc-list/mwc-list-item-css";
import { ListItemBase } from "@material/mwc-list/mwc-list-item-base";
import { css, CSSResult, customElement, property } from "lit-element";
import { html } from "lit-html";
@customElement("ha-clickable-list-item")
export class HaClickableListItem extends ListItemBase {
public href?: string;
public disableHref = false;
// property used only in css
@property({ type: Boolean, reflect: true }) public rtl = false;
public render() {
const r = super.render();
const href = this.href ? `/${this.href}` : "";
return html` ${this.renderRipple()}
${this.disableHref
? html`<a aria-role="option">${r}</a>`
: html`<a aria-role="option" href=${href}>${r}</a>`}`;
}
static get styles(): CSSResult[] {
return [
style,
css`
:host {
padding-left: 0px;
padding-right: 0px;
}
:host([rtl]) span {
margin-left: var(--mdc-list-item-graphic-margin, 20px) !important;
margin-right: 0px !important;
}
: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, 20px);
padding-right: var(--mdc-list-side-padding, 20px);
font-weight: 500;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-clickable-list-item": HaClickableListItem;
}
}

View File

@@ -1,5 +1,11 @@
import "./ha-clickable-list-item";
import "./ha-icon";
import "./ha-menu-button";
import "./ha-svg-icon";
import "./user/ha-user-badge";
import "@material/mwc-button/mwc-button";
import "@material/mwc-icon-button";
import { List } from "@material/mwc-list";
import {
mdiBell,
mdiCellphoneCog,
@@ -9,10 +15,6 @@ import {
mdiPlus,
mdiViewDashboard,
} 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 {
css,
CSSResult,
@@ -23,8 +25,8 @@ import {
LitElement,
property,
PropertyValues,
query,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import { guard } from "lit-html/directives/guard";
import memoizeOne from "memoize-one";
import { LocalStorage } from "../common/decorators/local-storage";
@@ -44,10 +46,8 @@ import {
import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive";
import { haStyleScrollbar } from "../resources/styles";
import type { HomeAssistant, PanelInfo } from "../types";
import "./ha-icon";
import "./ha-menu-button";
import "./ha-svg-icon";
import "./user/ha-user-badge";
import { ListItem } from "@material/mwc-list/mwc-list-item";
import { navigate } from "../common/navigate";
const SHOW_AFTER_SPACER = ["config", "developer-tools", "hassio"];
@@ -157,6 +157,12 @@ const computePanels = memoizeOne(
}
);
const isListItem = (element: Element): element is ListItem =>
element.hasAttribute("mwc-list-item");
const isNodeElement = (node: Node): node is Element =>
node.nodeType === Node.ELEMENT_NODE;
let Sortable;
@customElement("ha-sidebar")
@@ -181,6 +187,12 @@ class HaSidebar extends LitElement {
@internalProperty() private _renderEmptySortable = false;
@query("div.ha-scrollbar mwc-list.main-panels", false)
private _standardPanelList!: List;
@query("div.ha-scrollbar mwc-list.utility-panels", false)
private _utilityPanelList!: List;
private _mouseLeaveTimeout?: number;
private _tooltipHideTimeout?: number;
@@ -209,11 +221,21 @@ class HaSidebar extends LitElement {
// prettier-ignore
return html`
${this._renderHeader()}
<div class="ha-scrollbar">
${this._renderAllPanels()}
${this._renderDivider()}
${this._renderNotifications()}
${this._renderUserItem()}
<div disabled class="bottom-spacer"></div>
</div>
<mwc-list
attr-for-selected="data-panel"
@focusin=${this._listboxFocusIn}
@focusout=${this._listboxFocusOut}
@scroll=${this._listboxScroll}
@keydown=${this._listboxKeydown}
>
<li divider role="separator"></li>
${this._renderNotifications()}
${this._renderUserItem()}
${this._renderSpacer()}
</mwc-list>
<div class="tooltip"></div>
`;
}
@@ -289,7 +311,9 @@ class HaSidebar extends LitElement {
return;
}
if (!oldHass || oldHass.panelUrl !== this.hass.panelUrl) {
const selectedEl = this.shadowRoot!.querySelector(".iron-selected");
const selectedEl = this.shadowRoot!.querySelector(
"ha-clickable-list-item[activated]"
);
if (selectedEl) {
// @ts-ignore
selectedEl.scrollIntoViewIfNeeded();
@@ -329,7 +353,15 @@ class HaSidebar extends LitElement {
}
private _renderAllPanels() {
const [beforeSpacer, afterSpacer] = computePanels(
// prettier-ignore
return html`
${this._renderNormalPanels()}
${this._renderUtilityPanels()}
`;
}
private _renderNormalPanels() {
const [beforeSpacer] = computePanels(
this.hass.panels,
this.hass.defaultPanel,
this._panelOrder,
@@ -338,10 +370,9 @@ class HaSidebar extends LitElement {
// prettier-ignore
return html`
<paper-listbox
<mwc-list
class="main-panels"
attr-for-selected="data-panel"
class="ha-scrollbar"
.selected=${this.hass.panelUrl}
@focusin=${this._listboxFocusIn}
@focusout=${this._listboxFocusOut}
@scroll=${this._listboxScroll}
@@ -350,10 +381,7 @@ class HaSidebar extends LitElement {
${this.editMode
? this._renderPanelsEdit(beforeSpacer)
: this._renderPanels(beforeSpacer)}
${this._renderSpacer()}
${this._renderPanels(afterSpacer)}
${this._renderExternalConfiguration()}
</paper-listbox>
</mwc-list>
`;
}
@@ -364,7 +392,7 @@ class HaSidebar extends LitElement {
this._renderEmptySortable ? "" : this._renderPanels(beforeSpacer)
)}
</div>
${this._renderSpacer()}
${this._renderHiddenItemSpacer()}
${this._renderHiddenPanels()} `;
}
@@ -375,13 +403,15 @@ class HaSidebar extends LitElement {
if (!panel) {
return "";
}
return html`<paper-icon-item
return html`<ha-clickable-list-item
@click=${this._unhidePanel}
class="hidden-panel"
.panel=${url}
graphic="icon"
.rtl=${this.rtl}
>
<ha-icon
slot="item-icon"
slot="graphic"
.icon=${panel.url_path === this.hass.defaultPanel
? "mdi:view-dashboard"
: panel.icon}
@@ -395,18 +425,52 @@ class HaSidebar extends LitElement {
<mwc-icon-button class="show-panel">
<ha-svg-icon .path=${mdiPlus}></ha-svg-icon>
</mwc-icon-button>
</paper-icon-item>`;
</ha-clickable-list-item>`;
})}
${this._renderSpacer()}`
: ""}`;
}
private _renderDivider() {
return html`<div class="divider"></div>`;
private _renderUtilityPanels() {
const [, afterSpacer] = computePanels(
this.hass.panels,
this.hass.defaultPanel,
this._panelOrder,
this._hiddenPanels
);
// prettier-ignore
return html`
<mwc-list
@focusin=${this._listboxFocusIn}
@focusout=${this._listboxFocusOut}
@scroll=${this._listboxScroll}
@keydown=${this._listboxKeydown}
class="utility-panels"
>
${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
)
)}
${this._renderExternalConfiguration()}
</mwc-list>
`;
}
private _renderSpacer() {
return html`<div class="spacer" disabled></div>`;
return html`<li divider role="separator" class="spacer"></li>`;
}
private _renderHiddenItemSpacer() {
return html`<li divider role="separator" class="spacer-hidden"></li>`;
}
private _renderNotifications() {
@@ -419,20 +483,20 @@ class HaSidebar extends LitElement {
}
}
return html` <div
class="notifications-container"
@mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave}
>
<paper-icon-item
class="notifications"
return html`
<ha-clickable-list-item
aria-role="option"
@click=${this._handleShowNotificationDrawer}
graphic="icon"
hasMeta
@mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave}
.rtl=${this.rtl}
>
<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
? html`
<span class="notification-badge" slot="item-icon">
<span class="notification-badge">
${notificationCount}
</span>
`
@@ -441,45 +505,45 @@ class HaSidebar extends LitElement {
${this.hass.localize("ui.notification_drawer.title")}
</span>
${this.expanded && notificationCount > 0
? html` <span class="notification-badge">${notificationCount}</span> `
? html`
<span class="notification-badge" slot="meta"
>${notificationCount}</span
>
`
: ""}
</paper-icon-item>
</div>`;
</ha-clickable-list-item>
`;
}
private _renderUserItem() {
return html`<a
class=${classMap({
profile: true,
// Mimick behavior that paper-listbox provides
"iron-selected": this.hass.panelUrl === "profile",
})}
href="/profile"
return html`<ha-clickable-list-item
class="profile"
.href=${"profile"}
data-panel="panel"
tabindex="-1"
aria-role="option"
aria-label=${this.hass.localize("panel.profile")}
@mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave}
graphic="icon"
.activated=${this.hass.panelUrl === "profile"}
.rtl=${this.rtl}
>
<paper-icon-item>
<ha-user-badge
slot="item-icon"
.user=${this.hass.user}
.hass=${this.hass}
></ha-user-badge>
<ha-user-badge
slot="graphic"
.user=${this.hass.user}
.hass=${this.hass}
></ha-user-badge>
<span class="item-text">
${this.hass.user ? this.hass.user.name : ""}
</span>
</paper-icon-item>
</a>`;
<span class="item-text">
${this.hass.user?.name}
</span>
</ha-clickable-list-item> `;
}
private _renderExternalConfiguration() {
return html`${this._externalConfig && this._externalConfig.hasSettingsScreen
? html`
<a
<ha-clickable-list-item
aria-role="option"
aria-label=${this.hass.localize(
"ui.sidebar.external_app_configuration"
@@ -489,19 +553,17 @@ class HaSidebar extends LitElement {
@click=${this._handleExternalAppConfiguration}
@mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave}
graphic="icon"
.rtl=${this.rtl}
>
<paper-icon-item>
<ha-svg-icon
slot="item-icon"
.path=${mdiCellphoneCog}
></ha-svg-icon>
<span class="item-text">
${this.hass.localize("ui.sidebar.external_app_configuration")}
</span>
</paper-icon-item>
</a>
<ha-svg-icon slot="graphic" .path=${mdiCellphoneCog}></ha-svg-icon>
<span class="item-text">
${this.hass.localize("ui.sidebar.external_app_configuration")}
</span>
</ha-clickable-list-item>
`
: ""}`;
: ""}
${this._renderSpacer()} `;
}
private get _tooltip() {
@@ -520,7 +582,7 @@ class HaSidebar extends LitElement {
if (!Sortable) {
const [sortableImport, sortStylesImport] = await Promise.all([
import("sortablejs/modular/sortable.core.esm"),
import("../resources/ha-sortable-style"),
import("../resources/ha-sortable-style-ha-clickable"),
]);
const style = document.createElement("style");
@@ -542,7 +604,6 @@ class HaSidebar extends LitElement {
animation: 150,
fallbackClass: "sortable-fallback",
dataIdAttr: "data-panel",
handle: "paper-icon-item",
onSort: async () => {
this._panelOrder = this._sortable.toArray();
},
@@ -604,7 +665,7 @@ class HaSidebar extends LitElement {
clearTimeout(this._mouseLeaveTimeout);
this._mouseLeaveTimeout = undefined;
}
this._showTooltip(ev.currentTarget as PaperIconItemElement);
this._showTooltip(ev.currentTarget);
}
private _itemMouseLeave() {
@@ -620,7 +681,7 @@ class HaSidebar extends LitElement {
if (this.expanded || ev.target.nodeName !== "A") {
return;
}
this._showTooltip(ev.target.querySelector("paper-icon-item"));
this._showTooltip(ev.target.querySelector("ha-clickable-list-item"));
}
private _listboxFocusOut() {
@@ -640,17 +701,91 @@ class HaSidebar extends LitElement {
this._hideTooltip();
}
private _listboxKeydown() {
private _getIndexOfTarget(evt: Event): number {
const listbox = evt.currentTarget as List;
const elements = listbox.items;
const path = evt.composedPath();
for (const pathItem of path as Node[]) {
let index = -1;
if (isNodeElement(pathItem) && isListItem(pathItem)) {
index = elements.indexOf(pathItem);
}
if (index !== -1) {
return index;
}
}
return -1;
}
private _getCurrentListPosition(ev: KeyboardEvent) {
return {
index: this._getIndexOfTarget(ev),
list: ev.currentTarget as List,
};
}
private _selectNextItem(ev: KeyboardEvent) {
const [beforeSpacer, afterSpacer] = computePanels(
this.hass.panels,
this.hass.defaultPanel,
this._panelOrder,
this._hiddenPanels
);
const { index, list } = this._getCurrentListPosition(ev);
if (list === this._standardPanelList && index === beforeSpacer.length - 1) {
this._setFocusPanelList(this._utilityPanelList, "top");
} else if (
list === this._utilityPanelList &&
index === afterSpacer.length - 1
) {
this._setFocusPanelList(this._standardPanelList, "top");
}
}
private _selectPreviousItem(ev: KeyboardEvent) {
const { index, list } = this._getCurrentListPosition(ev);
if (list === this._standardPanelList && index === 0) {
this._setFocusPanelList(this._utilityPanelList, "bottom");
} else if (list === this._utilityPanelList && index === 0) {
this._setFocusPanelList(this._standardPanelList, "bottom");
}
}
private _listboxKeydown(ev: KeyboardEvent) {
if (ev.code === "ArrowDown") {
this._selectNextItem(ev);
} else if (ev.code === "ArrowUp") {
this._selectPreviousItem(ev);
} else if (ev.code === "Enter") {
(ev.target as ListItem)?.shadowRoot?.querySelector("a")?.click();
}
this._recentKeydownActiveUntil = new Date().getTime() + 100;
}
private _showTooltip(item: PaperIconItemElement) {
private _setFocusPanelList(list: List, position: "top" | "bottom") {
let index = 0;
if (position === "bottom") {
index = list.querySelectorAll("ha-clickable-list-item").length - 1;
}
list.focusItemAtIndex(index);
}
private _showTooltip(item) {
if (this._tooltipHideTimeout) {
clearTimeout(this._tooltipHideTimeout);
this._tooltipHideTimeout = undefined;
}
const tooltip = this._tooltip;
const listbox = this.shadowRoot!.querySelector("paper-listbox")!;
const listbox = this.shadowRoot!.querySelector("mwc-list")!;
let top = item.offsetTop + 11;
if (listbox.contains(item)) {
top -= listbox.scrollTop;
@@ -711,23 +846,22 @@ class HaSidebar extends LitElement {
iconPath?: string | null
) {
return html`
<a
aria-role="option"
href=${`/${urlPath}`}
<ha-clickable-list-item
.activated=${urlPath === this.hass.panelUrl}
.href=${urlPath}
.disableHref=${this.editMode}
data-panel=${urlPath}
tabindex="-1"
@mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave}
@click=${() => navigate(this, `/${urlPath}`)}
graphic="icon"
.rtl=${this.rtl}
>
<paper-icon-item>
${iconPath
? html`<ha-svg-icon
slot="item-icon"
.path=${iconPath}
></ha-svg-icon>`
: html`<ha-icon slot="item-icon" .icon=${icon}></ha-icon>`}
<span class="item-text">${title}</span>
</paper-icon-item>
${iconPath
? html`<ha-svg-icon slot="graphic" .path=${iconPath}></ha-svg-icon>`
: html`<ha-icon slot="graphic" .icon=${icon}></ha-icon>`}
${title}
${this.editMode
? html`<mwc-icon-button
class="hide-panel"
@@ -737,7 +871,7 @@ class HaSidebar extends LitElement {
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</mwc-icon-button>`
: ""}
</a>
</ha-clickable-list-item>
`;
}
@@ -795,6 +929,21 @@ class HaSidebar extends LitElement {
.menu mwc-icon-button {
color: var(--sidebar-icon-color);
}
ha-clickable-list-item {
margin: 4px;
border-radius: 4px;
height: 40px;
--mdc-list-side-padding: 12px;
--mdc-theme-text-icon-on-background: var(--sidebar-icon-color);
}
ha-clickable-list-item[activated] {
--mdc-theme-text-icon-on-background: var(
--sidebar-selected-icon-color
);
}
.title {
margin-left: 19px;
width: 100%;
@@ -806,7 +955,6 @@ class HaSidebar extends LitElement {
}
:host([narrow]) .title {
margin: 0;
padding: 0 16px;
}
:host([expanded]) .title {
display: initial;
@@ -822,120 +970,46 @@ class HaSidebar extends LitElement {
display: none;
}
paper-listbox {
padding: 4px 0;
display: flex;
flex-direction: column;
box-sizing: border-box;
height: calc(100% - var(--header-height) - 132px);
.ha-scrollbar {
height: calc(100% - var(--header-height) - 105px);
height: calc(
100% - var(--header-height) - 132px - env(safe-area-inset-bottom)
100% - var(--header-height) - 105px - env(safe-area-inset-bottom)
);
overflow-x: hidden;
background: none;
margin-left: env(safe-area-inset-left);
display: flex;
justify-content: space-between;
flex-direction: column;
}
:host([rtl]) paper-listbox {
mwc-list {
width: var(--app-drawer-width);
--mdc-list-vertical-padding: 4px 0;
margin-left: env(safe-area-inset-left);
-ms-user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
background-color: var(--sidebar-background-color);
}
:host([rtl]) mwc-list {
border-right: 0;
/* border-left: 1px solid var(--divider-color); */
}
:host([expanded]) mwc-list {
width: 256px;
width: calc(256px + env(safe-area-inset-left));
}
[slot="graphic"] {
width: 100%;
}
:host([rtl]) mwc-list {
margin-left: initial;
margin-right: env(safe-area-inset-right);
}
a {
text-decoration: none;
color: var(--sidebar-text-color);
font-weight: 500;
font-size: 14px;
position: relative;
display: block;
outline: 0;
}
paper-icon-item {
box-sizing: border-box;
margin: 4px;
padding-left: 12px;
border-radius: 4px;
--paper-item-min-height: 40px;
width: 48px;
}
:host([expanded]) paper-icon-item {
width: 248px;
}
:host([rtl]) paper-icon-item {
padding-left: auto;
padding-right: 12px;
}
ha-icon[slot="item-icon"],
ha-svg-icon[slot="item-icon"] {
color: var(--sidebar-icon-color);
}
.iron-selected paper-icon-item::before,
a:not(.iron-selected):focus::before {
border-radius: 4px;
position: absolute;
top: 0;
right: 2px;
bottom: 0;
left: 2px;
pointer-events: none;
content: "";
transition: opacity 15ms linear;
will-change: opacity;
}
.iron-selected paper-icon-item::before {
background-color: var(--sidebar-selected-icon-color);
opacity: 0.12;
}
a:not(.iron-selected):focus::before {
background-color: currentColor;
opacity: var(--dark-divider-opacity);
margin: 4px 8px;
}
.iron-selected paper-icon-item:focus::before,
.iron-selected:focus paper-icon-item::before {
opacity: 0.2;
}
.iron-selected paper-icon-item[pressed]:before {
opacity: 0.37;
}
paper-icon-item span {
color: var(--sidebar-text-color);
font-weight: 500;
font-size: 14px;
}
a.iron-selected paper-icon-item ha-icon,
a.iron-selected paper-icon-item ha-svg-icon {
color: var(--sidebar-selected-icon-color);
}
a.iron-selected .item-text {
color: var(--sidebar-selected-text-color);
}
paper-icon-item .item-text {
display: none;
max-width: calc(100% - 56px);
}
:host([expanded]) paper-icon-item .item-text {
display: block;
}
.divider {
bottom: 112px;
padding: 10px 0;
}
.divider::before {
content: " ";
display: block;
height: 1px;
background-color: var(--divider-color);
}
.notifications-container {
display: flex;
margin-left: env(safe-area-inset-left);
@@ -951,22 +1025,19 @@ class HaSidebar extends LitElement {
flex: 1;
}
.profile {
margin-left: env(safe-area-inset-left);
--mdc-list-item-graphic-margin: 16px;
--mdc-list-item-graphic-size: 40px;
--mdc-list-side-padding: 4px;
}
:host([rtl]) .profile {
margin-left: initial;
margin-right: env(safe-area-inset-right);
}
.profile paper-icon-item {
padding-left: 4px;
}
:host([rtl]) .profile paper-icon-item {
padding-left: auto;
padding-right: 4px;
--mdc-list-item-graphic-size: 40px;
--mdc-list-side-padding: 4px;
}
.profile .item-text {
margin-left: 8px;
}
:host([rtl]) .profile .item-text {
margin-right: 8px;
}
@@ -977,21 +1048,31 @@ class HaSidebar extends LitElement {
border-radius: 50%;
font-weight: 400;
background-color: var(--accent-color);
line-height: 20px;
line-height: 1.5rem;
text-align: center;
padding: 0px 6px;
padding: 2px 6px;
color: var(--text-accent-color, var(--text-primary-color));
font-size: 14px;
}
ha-svg-icon + .notification-badge {
position: absolute;
bottom: 14px;
left: 26px;
font-size: 0.65em;
bottom: 18px;
left: 25px;
padding: 0px 0px;
}
.spacer {
flex: 1;
pointer-events: none;
border: 0px;
}
.spacer-hidden {
flex: 1;
pointer-events: none;
height: 77px;
border: 0px;
}
.subheader {
@@ -1002,19 +1083,6 @@ class HaSidebar extends LitElement {
white-space: nowrap;
}
.dev-tools {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 0 8px;
width: 256px;
box-sizing: border-box;
}
.dev-tools a {
color: var(--sidebar-icon-color);
}
.tooltip {
display: none;
position: absolute;

View File

@@ -0,0 +1,95 @@
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;
opacity: 0;
}
.sortable-ghost {
opacity: 0.4;
}
@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 {
color: var(--secondary-text-color);
cursor: pointer;
}
`;

View File

@@ -1,6 +1,7 @@
import { css } from "lit-element";
export const sortableStyles = css`
#sortable ha-clickable-list-item:nth-of-type(2n),
#sortable a:nth-of-type(2n) paper-icon-item {
animation-name: keyframes1;
animation-iteration-count: infinite;
@@ -9,6 +10,7 @@ export const sortableStyles = css`
animation-duration: 0.25s;
}
#sortable ha-clickable-list-item:nth-of-type(2n-1),
#sortable a:nth-of-type(2n-1) paper-icon-item {
animation-name: keyframes2;
animation-iteration-count: infinite;
@@ -34,16 +36,13 @@ export const sortableStyles = css`
.sortable-fallback {
display: none;
opacity: 0;
}
.sortable-ghost {
opacity: 0.4;
}
.sortable-fallback {
opacity: 0;
}
@keyframes keyframes1 {
0% {
transform: rotate(-1deg);
@@ -73,15 +72,10 @@ export const sortableStyles = css`
display: none;
position: absolute;
top: 0;
right: 4px;
right: 0;
--mdc-icon-button-size: 40px;
}
:host([rtl]) .show-panel {
right: initial;
left: 4px;
}
.hide-panel {
top: 4px;
right: 8px;
@@ -100,6 +94,8 @@ export const sortableStyles = css`
display: inline-flex;
}
ha-clickable-list-item.hidden-panel,
ha-clickable-list-item.hidden-panel span,
paper-icon-item.hidden-panel,
paper-icon-item.hidden-panel span,
paper-icon-item.hidden-panel ha-icon[slot="item-icon"] {