Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update sidebar #16017

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
bfed613
initial sidebar refactoring work, todo: sorting
KTibow Apr 1, 2023
e6f0319
add editing and fix stuff
KTibow Apr 2, 2023
36779f9
rename some stuff and add external config
KTibow Apr 2, 2023
d484b5f
fix focusing the list of items
KTibow Apr 2, 2023
fe5025f
fix rtl (the old sidebar had a rtl bug, fun fact)
KTibow Apr 3, 2023
25ca09d
Merge branch 'dev' into dev
KTibow Apr 5, 2023
4d3bb5f
Merge branch 'dev' into dev
KTibow Jun 2, 2023
45a8797
Fix accessibility and other bugs
KTibow Jun 3, 2023
5846073
Merge branch 'dev' into dev
KTibow Jul 6, 2023
07351e6
use @storage
KTibow Jul 6, 2023
a0f5d4b
improve scrolling
KTibow Aug 2, 2023
364ad1a
Merge branch 'dev' into dev
KTibow Aug 25, 2023
3f1fe22
fix most stuff (TODO: wiggling)
KTibow Aug 25, 2023
586e12f
somewhat improve sorting
KTibow Aug 25, 2023
89482a0
Merge branch 'home-assistant:dev' into dev
KTibow Sep 17, 2023
abb8ca1
Merge remote-tracking branch 'upstream/dev' into dev
KTibow Oct 3, 2023
30b8631
fix a11y
KTibow Oct 3, 2023
c7d72c1
Merge branch 'dev' into dev
KTibow Oct 19, 2023
788eca1
Remove useless slots
KTibow Oct 20, 2023
b38d350
Fix selected
KTibow Oct 21, 2023
6c47aed
Fix RTL
KTibow Oct 21, 2023
471ffef
Merge branch 'dev' into dev
KTibow Oct 21, 2023
fcab8c9
Merge remote-tracking branch 'upstream/dev' into dev
KTibow Oct 31, 2023
5f6d718
format code
KTibow Oct 31, 2023
e154267
Merge remote-tracking branch 'upstream/dev' into dev
KTibow Dec 3, 2023
70ac929
fix formatting
KTibow Dec 3, 2023
74e2f65
address review
KTibow Dec 5, 2023
7de61b9
address review (make keydown/keyup static)
KTibow Dec 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 187 additions & 0 deletions src/components/ha-sidebar-edit-panels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import { mdiClose, mdiPlus } from "@mdi/js";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import type { SortableInstance } from "../resources/sortable";
import { HomeAssistant } from "../types";
import "./ha-icon";
import "./ha-icon-button";
import "./ha-svg-icon";

const styles = css`
:host {
display: flex;
flex-direction: column;
padding: 0 12px;
}
.panel {
--rgb-text: var(--rgb-sidebar-text-color);
background-color: transparent;
color: rgb(var(--rgb-text));
font-family: inherit;
border: none;
cursor: pointer;
width: 100%;
font-weight: 500;
font-size: 14px;
line-height: 20px;

box-sizing: border-box;
display: flex;
align-items: center;
padding: 0 16px;
border-radius: var(--sidebar-item-radius, 25px);
height: 50px;
}
.panel > .icon {
display: flex;
width: 36px;
text-align: left;
}
.panel > ha-icon-button {
margin-left: auto;
margin-right: -12px;
margin-inline: auto -12px;
}
.panel:hover {
color: rgb(var(--rgb-text));
background-color: rgba(var(--rgb-text), 0.08);
}
.panel:focus-visible,
.panel:active {
color: rgb(var(--rgb-text));
background-color: rgba(var(--rgb-text), 0.12);
}
#sortable {
overflow: visible;
}
#sortable .panel {
cursor: grab;
}
.sortable-fallback {
display: none;
}
.sortable-ghost {
opacity: 0.4;
}
#sortable .panel:nth-child(even) {
animation: keyframes1 infinite 0.37s;
transform-origin: 50% 10%;
}
#sortable .panel:nth-child(odd) {
animation: keyframes2 infinite alternate 0.5s 0.15s;
transform-origin: 30% 5%;
}
@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;
}
}
`;

@customElement("ha-sidebar-edit-panels")
class HaSidebarEditPanels extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;

@property() public panels: any[] = [];

@property() public hiddenPanels: any[] = [];

private _sortable?: SortableInstance;

protected render() {
const renderPanel = (panel) =>
html`<div class="panel" data-panel=${panel.url_path}>
<span class="icon">
${panel.icon
? html`<ha-icon .icon=${panel.icon}></ha-icon>`
: html`<ha-svg-icon .path=${panel.iconPath}></ha-svg-icon>`}
</span>
${panel.name}
<ha-icon-button
.label=${this.hass.localize("ui.sidebar.hide_panel")}
.path=${mdiClose}
@click=${this._hidePanel}
></ha-icon-button>
</div>`;
const renderHiddenPanel = (panel) =>
html`<button
class="panel"
data-panel=${panel.url_path}
title=${this.hass.localize("ui.sidebar.show_panel")}
@click=${this._showPanel}
>
<span class="icon">
${panel.icon
? html`<ha-icon .icon=${panel.icon}></ha-icon>`
: html`<ha-svg-icon .path=${panel.iconPath}></ha-svg-icon>`}
</span>
${panel.name}
<ha-svg-icon .path=${mdiPlus}></ha-svg-icon>
</button>`;
return html`<div id="sortable">${this.panels.map(renderPanel)}</div>
${this.hiddenPanels.map(renderHiddenPanel)}`;
}

protected async firstUpdated() {
const { default: Sortable } = await import("../resources/sortable");
this._sortable = new Sortable(
this.shadowRoot!.getElementById("sortable")!,
{
animation: 150,
dataIdAttr: "data-panel",
handle: "div",
onSort: () => {
fireEvent(this, "panel-reorder", this._sortable!.toArray());
},
}
);
}

private _hidePanel(ev: CustomEvent) {
const panel = (ev.target as HTMLElement)
.closest("[data-panel]")
?.getAttribute("data-panel");
if (!panel) return;
fireEvent(this, "panel-hide", panel);
}

private _showPanel(ev: CustomEvent) {
const panel = (ev.target as HTMLElement)
.closest("[data-panel]")
?.getAttribute("data-panel");
if (!panel) return;
fireEvent(this, "panel-show", panel);
}

static styles = styles;
}

declare global {
interface HTMLElementTagNameMap {
"ha-sidebar-edit-panels": HaSidebarEditPanels;
}
interface HASSDomEvents {
"panel-reorder": string[];
"panel-hide": string;
"panel-show": string;
}
}
90 changes: 90 additions & 0 deletions src/components/ha-sidebar-panel-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import "@material/mwc-button/mwc-button";
import { mdiCog } from "@mdi/js";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import { throttle } from "../common/util/throttle";
import { subscribeRepairsIssueRegistry } from "../data/repairs";
import { updateCanInstall, UpdateEntity } from "../data/update";
import { haStyleSidebarItem } from "../resources/styles";
import { HomeAssistant } from "../types";
import "./ha-svg-icon";
import { keydown, keyup } from "../resources/button-handlers";

const styles = css`
.item.expanded {
width: 100%;
}
`;

@customElement("ha-sidebar-panel-config")
class HaSidebarPanelConfig extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;

@property() public name = "";

@property({ type: Boolean }) public expanded = false;

@property({ type: Boolean }) public selected = false;

@state() private _updatesCount = 0;

@state() private _issuesCount = 0;

protected render() {
const notices = this._updatesCount + this._issuesCount;
return html`<a
href="/config"
aria-label=${this.name}
aria-current=${this.selected ? "page" : "false"}
class="item ${this.expanded ? "expanded" : ""}"
@keydown=${keydown}
@keyup=${keyup}
>
<div class="target"></div>
<span class="icon">
<ha-svg-icon .path=${mdiCog}></ha-svg-icon>
${!this.expanded && notices > 0
? html`<span class="badge">${notices}</span>`
: ""}
</span>
<span class="name">${this.name}</span>
${this.expanded && notices > 0
? html`<span class="count">${notices}</span>`
: ""}
</a>`;
}

protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
this._checkUpdates();
}

private _checkUpdates = throttle(() => {
this._updatesCount = Object.keys(this.hass.states).filter(
(e) =>
e.startsWith("update.") &&
updateCanInstall(this.hass.states[e] as UpdateEntity)
).length;
}, 5000);

public hassSubscribe(): UnsubscribeFunc[] {
return this.hass.user?.is_admin
? [
subscribeRepairsIssueRegistry(this.hass.connection!, (repairs) => {
this._issuesCount = repairs.issues.filter(
(issue) => !issue.ignored
).length;
}),
]
: [];
}

static styles = [haStyleSidebarItem, styles];
}

declare global {
interface HTMLElementTagNameMap {
"ha-sidebar-panel-config": HaSidebarPanelConfig;
}
}
51 changes: 51 additions & 0 deletions src/components/ha-sidebar-panel-ext-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import "@material/mwc-button/mwc-button";
import { mdiCellphoneCog } from "@mdi/js";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { haStyleSidebarItem } from "../resources/styles";
import "./ha-icon";
import "./ha-svg-icon";
import { HomeAssistant } from "../types";

const styles = css`
.item {
width: 100%;
}
`;

@customElement("ha-sidebar-panel-ext-config")
class HaSidebarPanelExtConfig extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;

@property() public name = "";

@property({ type: Boolean }) public expanded = false;

protected render() {
return html`<button
class="item ${this.expanded ? "expanded" : ""}"
aria-label=${this.name}
@click=${this._showConfig}
>
<div class="target"></div>
<span class="icon">
<ha-svg-icon .path=${mdiCellphoneCog}></ha-svg-icon>
</span>
<span class="name">${this.name}</span>
</button>`;
}

private _showConfig() {
this.hass.auth.external!.fireMessage({
type: "config_screen/show",
});
}

static styles = [haStyleSidebarItem, styles];
}

declare global {
interface HTMLElementTagNameMap {
"ha-sidebar-panel-ext-config": HaSidebarPanelExtConfig;
}
}
Loading
Loading