mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 07:16:39 +00:00
Edit sidebar in a dialog (#25532)
This commit is contained in:
parent
f0beef22d2
commit
32b3c83337
@ -25,6 +25,7 @@ export interface DisplayItem {
|
||||
value: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
disableSorting?: boolean;
|
||||
}
|
||||
|
||||
export interface DisplayValue {
|
||||
@ -50,6 +51,9 @@ export class HaItemDisplayEditor extends LitElement {
|
||||
@property({ type: Boolean, attribute: "show-navigation-button" })
|
||||
public showNavigationButton = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "dont-sort-visible" })
|
||||
public dontSortVisible = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
public value: DisplayValue = {
|
||||
order: [],
|
||||
@ -122,9 +126,15 @@ export class HaItemDisplayEditor extends LitElement {
|
||||
private _visibleItems = memoizeOne(
|
||||
(items: DisplayItem[], hidden: string[], order: string[]) => {
|
||||
const compare = orderCompare(order);
|
||||
return items
|
||||
.filter((item) => !hidden.includes(item.value))
|
||||
.sort((a, b) => compare(a.value, b.value));
|
||||
|
||||
const visibleItems = items.filter((item) => !hidden.includes(item.value));
|
||||
if (this.dontSortVisible) {
|
||||
return visibleItems;
|
||||
}
|
||||
|
||||
return items.sort((a, b) =>
|
||||
a.disableSorting && !b.disableSorting ? -1 : compare(a.value, b.value)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@ -160,7 +170,14 @@ export class HaItemDisplayEditor extends LitElement {
|
||||
(item) => item.value,
|
||||
(item: DisplayItem, _idx) => {
|
||||
const isVisible = !this.value.hidden.includes(item.value);
|
||||
const { label, value, description, icon, iconPath } = item;
|
||||
const {
|
||||
label,
|
||||
value,
|
||||
description,
|
||||
icon,
|
||||
iconPath,
|
||||
disableSorting,
|
||||
} = item;
|
||||
return html`
|
||||
<ha-md-list-item
|
||||
type=${ifDefined(
|
||||
@ -172,14 +189,14 @@ export class HaItemDisplayEditor extends LitElement {
|
||||
.value=${value}
|
||||
class=${classMap({
|
||||
hidden: !isVisible,
|
||||
draggable: isVisible,
|
||||
draggable: isVisible && !disableSorting,
|
||||
})}
|
||||
>
|
||||
<span slot="headline">${label}</span>
|
||||
${description
|
||||
? html`<span slot="supporting-text">${description}</span>`
|
||||
: nothing}
|
||||
${isVisible
|
||||
${isVisible && !disableSorting
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
class="handle"
|
||||
|
@ -1,11 +1,9 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import {
|
||||
mdiBell,
|
||||
mdiCalendar,
|
||||
mdiCellphoneCog,
|
||||
mdiChartBox,
|
||||
mdiClipboardList,
|
||||
mdiClose,
|
||||
mdiCog,
|
||||
mdiFormatListBulletedType,
|
||||
mdiHammer,
|
||||
@ -13,12 +11,11 @@ import {
|
||||
mdiMenu,
|
||||
mdiMenuOpen,
|
||||
mdiPlayBoxMultiple,
|
||||
mdiPlus,
|
||||
mdiTooltipAccount,
|
||||
mdiViewDashboard,
|
||||
} from "@mdi/js";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { CSSResult, CSSResultGroup, PropertyValues } from "lit";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import {
|
||||
customElement,
|
||||
@ -29,7 +26,6 @@ import {
|
||||
} from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { storage } from "../common/decorators/storage";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { toggleAttribute } from "../common/dom/toggle_attribute";
|
||||
import { stringCompare } from "../common/string/compare";
|
||||
@ -40,6 +36,7 @@ import { subscribeNotifications } from "../data/persistent_notification";
|
||||
import { subscribeRepairsIssueRegistry } from "../data/repairs";
|
||||
import type { UpdateEntity } from "../data/update";
|
||||
import { updateCanInstall } from "../data/update";
|
||||
import { showEditSidebarDialog } from "../dialogs/sidebar/show-dialog-edit-sidebar";
|
||||
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
||||
import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive";
|
||||
import { haStyleScrollbar } from "../resources/styles";
|
||||
@ -49,8 +46,6 @@ import "./ha-icon-button";
|
||||
import "./ha-md-list";
|
||||
import "./ha-md-list-item";
|
||||
import type { HaMdListItem } from "./ha-md-list-item";
|
||||
import "./ha-menu-button";
|
||||
import "./ha-sortable";
|
||||
import "./ha-svg-icon";
|
||||
import "./user/ha-user-badge";
|
||||
|
||||
@ -67,7 +62,7 @@ const SORT_VALUE_URL_PATHS = {
|
||||
config: 11,
|
||||
};
|
||||
|
||||
const PANEL_ICONS = {
|
||||
export const PANEL_ICONS = {
|
||||
calendar: mdiCalendar,
|
||||
"developer-tools": mdiHammer,
|
||||
energy: mdiLightningBolt,
|
||||
@ -140,7 +135,7 @@ const defaultPanelSorter = (
|
||||
return stringCompare(a.title!, b.title!, language);
|
||||
};
|
||||
|
||||
const computePanels = memoizeOne(
|
||||
export const computePanels = memoizeOne(
|
||||
(
|
||||
panels: HomeAssistant["panels"],
|
||||
defaultPanel: HomeAssistant["defaultPanel"],
|
||||
@ -192,8 +187,11 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
@property({ attribute: "always-expand", type: Boolean })
|
||||
public alwaysExpand = false;
|
||||
|
||||
@property({ attribute: "edit-mode", type: Boolean })
|
||||
public editMode = false;
|
||||
@property({ attribute: false })
|
||||
public panelOrder!: string[];
|
||||
|
||||
@property({ attribute: false })
|
||||
public hiddenPanels!: string[];
|
||||
|
||||
@state() private _notifications?: PersistentNotification[];
|
||||
|
||||
@ -207,26 +205,8 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
|
||||
private _recentKeydownActiveUntil = 0;
|
||||
|
||||
private _editStyleLoaded = false;
|
||||
|
||||
private _unsubPersistentNotifications: UnsubscribeFunc | undefined;
|
||||
|
||||
@state()
|
||||
@storage({
|
||||
key: "sidebarPanelOrder",
|
||||
state: true,
|
||||
subscribe: true,
|
||||
})
|
||||
private _panelOrder: string[] = [];
|
||||
|
||||
@state()
|
||||
@storage({
|
||||
key: "sidebarHiddenPanels",
|
||||
state: true,
|
||||
subscribe: true,
|
||||
})
|
||||
private _hiddenPanels: string[] = [];
|
||||
|
||||
@query(".tooltip") private _tooltip!: HTMLDivElement;
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
@ -270,13 +250,12 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
changedProps.has("expanded") ||
|
||||
changedProps.has("narrow") ||
|
||||
changedProps.has("alwaysExpand") ||
|
||||
changedProps.has("editMode") ||
|
||||
changedProps.has("_externalConfig") ||
|
||||
changedProps.has("_updatesCount") ||
|
||||
changedProps.has("_issuesCount") ||
|
||||
changedProps.has("_notifications") ||
|
||||
changedProps.has("_hiddenPanels") ||
|
||||
changedProps.has("_panelOrder")
|
||||
changedProps.has("hiddenPanels") ||
|
||||
changedProps.has("panelOrder")
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@ -322,9 +301,6 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
if (changedProps.has("alwaysExpand")) {
|
||||
toggleAttribute(this, "expanded", this.alwaysExpand);
|
||||
}
|
||||
if (changedProps.has("editMode") && this.editMode) {
|
||||
this._editModeActivated();
|
||||
}
|
||||
if (!changedProps.has("hass")) {
|
||||
return;
|
||||
}
|
||||
@ -374,8 +350,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
class="menu"
|
||||
@action=${this._handleAction}
|
||||
.actionHandler=${actionHandler({
|
||||
hasHold: !this.editMode,
|
||||
disabled: this.editMode,
|
||||
hasHold: true,
|
||||
})}
|
||||
>
|
||||
${!this.narrow
|
||||
@ -389,11 +364,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
></ha-icon-button>
|
||||
`
|
||||
: ""}
|
||||
${this.editMode
|
||||
? html`<mwc-button outlined @click=${this._closeEditMode}>
|
||||
${this.hass.localize("ui.sidebar.done")}
|
||||
</mwc-button>`
|
||||
: html`<div class="title">Home Assistant</div>`}
|
||||
<div class="title">Home Assistant</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@ -401,14 +372,13 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
const [beforeSpacer, afterSpacer] = computePanels(
|
||||
this.hass.panels,
|
||||
this.hass.defaultPanel,
|
||||
this._panelOrder,
|
||||
this._hiddenPanels,
|
||||
this.panelOrder,
|
||||
this.hiddenPanels,
|
||||
this.hass.locale
|
||||
);
|
||||
|
||||
// prettier-ignore
|
||||
return html`
|
||||
<ha-sortable .disabled=${!this.editMode} draggable-selector=".draggable" @item-moved=${this._panelMoved}>
|
||||
<ha-md-list
|
||||
class="ha-scrollbar"
|
||||
@focusin=${this._listboxFocusIn}
|
||||
@ -416,22 +386,15 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
@scroll=${this._listboxScroll}
|
||||
@keydown=${this._listboxKeydown}
|
||||
>
|
||||
${this.editMode
|
||||
? this._renderPanelsEdit(beforeSpacer, selectedPanel)
|
||||
: this._renderPanels(beforeSpacer, selectedPanel)}
|
||||
${this._renderPanels(beforeSpacer, selectedPanel)}
|
||||
${this._renderSpacer()}
|
||||
${this._renderPanels(afterSpacer, selectedPanel)}
|
||||
${this._renderExternalConfiguration()}
|
||||
</ha-md-list>
|
||||
</ha-sortable>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderPanels(
|
||||
panels: PanelInfo[],
|
||||
selectedPanel: string,
|
||||
sortable = false
|
||||
) {
|
||||
private _renderPanels(panels: PanelInfo[], selectedPanel: string) {
|
||||
return panels.map((panel) =>
|
||||
this._renderPanel(
|
||||
panel.url_path,
|
||||
@ -444,36 +407,26 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
: panel.url_path in PANEL_ICONS
|
||||
? PANEL_ICONS[panel.url_path]
|
||||
: undefined,
|
||||
selectedPanel,
|
||||
sortable
|
||||
selectedPanel
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private _renderPanelsEdit(beforeSpacer: PanelInfo[], selectedPanel: string) {
|
||||
return html`
|
||||
${this._renderPanels(beforeSpacer, selectedPanel, true)}
|
||||
${this._renderSpacer()}${this._renderHiddenPanels()}
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderPanel(
|
||||
urlPath: string,
|
||||
title: string | null,
|
||||
icon: string | null | undefined,
|
||||
iconPath: string | null | undefined,
|
||||
selectedPanel: string,
|
||||
sortable = false
|
||||
selectedPanel: string
|
||||
) {
|
||||
return urlPath === "config"
|
||||
? this._renderConfiguration(title, selectedPanel)
|
||||
: html`
|
||||
<ha-md-list-item
|
||||
.href=${this.editMode ? undefined : `/${urlPath}`}
|
||||
.href=${`/${urlPath}`}
|
||||
type="link"
|
||||
class=${classMap({
|
||||
selected: selectedPanel === urlPath,
|
||||
draggable: this.editMode && sortable,
|
||||
})}
|
||||
@mouseenter=${this._itemMouseEnter}
|
||||
@mouseleave=${this._itemMouseLeave}
|
||||
@ -482,81 +435,10 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
? html`<ha-svg-icon slot="start" .path=${iconPath}></ha-svg-icon>`
|
||||
: html`<ha-icon slot="start" .icon=${icon}></ha-icon>`}
|
||||
<span class="item-text" slot="headline">${title}</span>
|
||||
${this.editMode
|
||||
? html`<ha-icon-button
|
||||
.label=${this.hass.localize("ui.sidebar.hide_panel")}
|
||||
.path=${mdiClose}
|
||||
class="hide-panel"
|
||||
.panel=${urlPath}
|
||||
@click=${this._hidePanel}
|
||||
slot="end"
|
||||
></ha-icon-button>`
|
||||
: nothing}
|
||||
</ha-md-list-item>
|
||||
`;
|
||||
}
|
||||
|
||||
private _panelMoved(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const { oldIndex, newIndex } = ev.detail;
|
||||
|
||||
const [beforeSpacer] = computePanels(
|
||||
this.hass.panels,
|
||||
this.hass.defaultPanel,
|
||||
this._panelOrder,
|
||||
this._hiddenPanels,
|
||||
this.hass.locale
|
||||
);
|
||||
|
||||
const panelOrder = beforeSpacer.map((panel) => panel.url_path);
|
||||
const panel = panelOrder.splice(oldIndex, 1)[0];
|
||||
panelOrder.splice(newIndex, 0, panel);
|
||||
|
||||
this._panelOrder = panelOrder;
|
||||
}
|
||||
|
||||
private _renderHiddenPanels() {
|
||||
return html`${this._hiddenPanels.length
|
||||
? html`${this._hiddenPanels.map((url) => {
|
||||
const panel = this.hass.panels[url];
|
||||
if (!panel) {
|
||||
return "";
|
||||
}
|
||||
return html`<ha-md-list-item
|
||||
@click=${this._unhidePanel}
|
||||
class="hidden-panel"
|
||||
.panel=${url}
|
||||
type="button"
|
||||
>
|
||||
${panel.url_path === this.hass.defaultPanel && !panel.icon
|
||||
? html`<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${PANEL_ICONS.lovelace}
|
||||
></ha-svg-icon>`
|
||||
: panel.url_path in PANEL_ICONS
|
||||
? html`<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${PANEL_ICONS[panel.url_path]}
|
||||
></ha-svg-icon>`
|
||||
: html`<ha-icon slot="start" .icon=${panel.icon}></ha-icon>`}
|
||||
<span class="item-text" slot="headline"
|
||||
>${panel.url_path === this.hass.defaultPanel
|
||||
? this.hass.localize("panel.states")
|
||||
: this.hass.localize(`panel.${panel.title}`) ||
|
||||
panel.title}</span
|
||||
>
|
||||
<ha-icon-button
|
||||
.label=${this.hass.localize("ui.sidebar.show_panel")}
|
||||
.path=${mdiPlus}
|
||||
class="show-panel"
|
||||
slot="end"
|
||||
></ha-icon-button>
|
||||
</ha-md-list-item>`;
|
||||
})}
|
||||
${this._renderSpacer()}`
|
||||
: ""}`;
|
||||
}
|
||||
|
||||
private _renderDivider() {
|
||||
return html`<div class="divider"></div>`;
|
||||
}
|
||||
@ -677,48 +559,17 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
fireEvent(this, "hass-edit-sidebar", { editMode: true });
|
||||
showEditSidebarDialog(this, {
|
||||
saveCallback: this._saveSidebar,
|
||||
});
|
||||
}
|
||||
|
||||
private async _editModeActivated() {
|
||||
await this._loadEditStyle();
|
||||
}
|
||||
|
||||
private async _loadEditStyle() {
|
||||
if (this._editStyleLoaded) return;
|
||||
|
||||
const editStylesImport = await import("../resources/ha-sidebar-edit-style");
|
||||
|
||||
const style = document.createElement("style");
|
||||
style.innerHTML = (editStylesImport.sidebarEditStyle as CSSResult).cssText;
|
||||
this.shadowRoot!.appendChild(style);
|
||||
|
||||
await this.updateComplete;
|
||||
}
|
||||
|
||||
private _closeEditMode() {
|
||||
fireEvent(this, "hass-edit-sidebar", { editMode: false });
|
||||
}
|
||||
|
||||
private async _hidePanel(ev: Event) {
|
||||
ev.preventDefault();
|
||||
const panel = (ev.currentTarget as any).panel;
|
||||
if (this._hiddenPanels.includes(panel)) {
|
||||
return;
|
||||
}
|
||||
// Make a copy for Memoize
|
||||
this._hiddenPanels = [...this._hiddenPanels, panel];
|
||||
// Remove it from the panel order
|
||||
this._panelOrder = this._panelOrder.filter((order) => order !== panel);
|
||||
}
|
||||
|
||||
private async _unhidePanel(ev: Event) {
|
||||
ev.preventDefault();
|
||||
const panel = (ev.currentTarget as any).panel;
|
||||
this._hiddenPanels = this._hiddenPanels.filter(
|
||||
(hidden) => hidden !== panel
|
||||
);
|
||||
}
|
||||
private _saveSidebar = (order: string[], hidden: string[]) => {
|
||||
fireEvent(this, "hass-edit-sidebar", {
|
||||
order,
|
||||
hidden,
|
||||
});
|
||||
};
|
||||
|
||||
private _itemMouseEnter(ev: MouseEvent) {
|
||||
// On keypresses on the listbox, we're going to ignore mouse enter events
|
||||
@ -875,12 +726,6 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
:host([expanded]) .title {
|
||||
display: initial;
|
||||
}
|
||||
:host([expanded]) .menu mwc-button {
|
||||
margin: 0 8px;
|
||||
}
|
||||
.menu mwc-button {
|
||||
width: 100%;
|
||||
}
|
||||
.hidden-panel {
|
||||
display: none;
|
||||
}
|
||||
|
159
src/dialogs/sidebar/dialog-edit-sidebar.ts
Normal file
159
src/dialogs/sidebar/dialog-edit-sidebar.ts
Normal file
@ -0,0 +1,159 @@
|
||||
import "@material/mwc-linear-progress/mwc-linear-progress";
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../../components/ha-dialog-header";
|
||||
import "../../components/ha-icon-button";
|
||||
import "../../components/ha-items-display-editor";
|
||||
import type { DisplayValue } from "../../components/ha-items-display-editor";
|
||||
import "../../components/ha-md-dialog";
|
||||
import type { HaMdDialog } from "../../components/ha-md-dialog";
|
||||
import { computePanels, PANEL_ICONS } from "../../components/ha-sidebar";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import type { EditSidebarDialogParams } from "./show-dialog-edit-sidebar";
|
||||
|
||||
@customElement("dialog-edit-sidebar")
|
||||
class DialogEditSidebar extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _open = false;
|
||||
|
||||
@query("ha-md-dialog") private _dialog?: HaMdDialog;
|
||||
|
||||
@state() private _order: string[] = [];
|
||||
|
||||
@state() private _hidden: string[] = [];
|
||||
|
||||
private _saveCallback?: (order: string[], hidden: string[]) => void;
|
||||
|
||||
public async showDialog(params: EditSidebarDialogParams): Promise<void> {
|
||||
this._open = true;
|
||||
|
||||
const storedOrder = localStorage.getItem("sidebarPanelOrder");
|
||||
const storedHidden = localStorage.getItem("sidebarHiddenPanels");
|
||||
|
||||
this._order = storedOrder ? JSON.parse(storedOrder) : this._order;
|
||||
this._hidden = storedHidden ? JSON.parse(storedHidden) : this._hidden;
|
||||
this._saveCallback = params.saveCallback;
|
||||
}
|
||||
|
||||
private _dialogClosed(): void {
|
||||
this._open = false;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._dialog?.close();
|
||||
}
|
||||
|
||||
private _panels = memoizeOne((panels: HomeAssistant["panels"]) =>
|
||||
panels ? Object.values(panels) : []
|
||||
);
|
||||
|
||||
protected render() {
|
||||
if (!this._open) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const dialogTitle = this.hass.localize("ui.sidebar.edit_sidebar");
|
||||
|
||||
const panels = this._panels(this.hass.panels);
|
||||
|
||||
const [beforeSpacer, afterSpacer] = computePanels(
|
||||
this.hass.panels,
|
||||
this.hass.defaultPanel,
|
||||
this._order,
|
||||
this._hidden,
|
||||
this.hass.locale
|
||||
);
|
||||
|
||||
const items = [
|
||||
...beforeSpacer,
|
||||
...panels.filter((panel) => this._hidden.includes(panel.url_path)),
|
||||
...afterSpacer.filter((panel) => panel.url_path !== "config"),
|
||||
].map((panel) => ({
|
||||
value: panel.url_path,
|
||||
label:
|
||||
panel.url_path === this.hass.defaultPanel
|
||||
? panel.title || this.hass.localize("panel.states")
|
||||
: this.hass.localize(`panel.${panel.title}`) || panel.title || "?",
|
||||
icon: panel.icon || undefined,
|
||||
iconPath:
|
||||
panel.url_path === this.hass.defaultPanel && !panel.icon
|
||||
? PANEL_ICONS.lovelace
|
||||
: panel.url_path in PANEL_ICONS
|
||||
? PANEL_ICONS[panel.url_path]
|
||||
: undefined,
|
||||
disableSorting: panel.url_path === "developer-tools",
|
||||
}));
|
||||
|
||||
return html`
|
||||
<ha-md-dialog open @closed=${this._dialogClosed}>
|
||||
<ha-dialog-header slot="headline">
|
||||
<ha-icon-button
|
||||
slot="navigationIcon"
|
||||
.label=${this.hass.localize("ui.common.close") ?? "Close"}
|
||||
.path=${mdiClose}
|
||||
@click=${this.closeDialog}
|
||||
></ha-icon-button>
|
||||
<span slot="title" .title=${dialogTitle}> ${dialogTitle} </span>
|
||||
</ha-dialog-header>
|
||||
<div slot="content" class="content">
|
||||
<ha-items-display-editor
|
||||
.hass=${this.hass}
|
||||
.value=${{
|
||||
order: this._order,
|
||||
hidden: this._hidden,
|
||||
}}
|
||||
.items=${items}
|
||||
@value-changed=${this._changed}
|
||||
dont-sort-visible
|
||||
>
|
||||
</ha-items-display-editor>
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<ha-button @click=${this.closeDialog}>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button @click=${this._save}>
|
||||
${this.hass.localize("ui.common.save")}
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-md-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _changed(ev: CustomEvent<{ value: DisplayValue }>): void {
|
||||
const { order = [], hidden = [] } = ev.detail.value;
|
||||
this._order = [...order];
|
||||
this._hidden = [...hidden];
|
||||
}
|
||||
|
||||
private _save(): void {
|
||||
this._saveCallback?.(this._order ?? [], this._hidden ?? []);
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-md-dialog {
|
||||
min-width: 600px;
|
||||
max-height: 90%;
|
||||
}
|
||||
|
||||
@media all and (max-width: 600px), all and (max-height: 500px) {
|
||||
ha-md-dialog {
|
||||
--md-dialog-container-shape: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-edit-sidebar": DialogEditSidebar;
|
||||
}
|
||||
}
|
18
src/dialogs/sidebar/show-dialog-edit-sidebar.ts
Normal file
18
src/dialogs/sidebar/show-dialog-edit-sidebar.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
|
||||
export interface EditSidebarDialogParams {
|
||||
saveCallback: (order: string[], hidden: string[]) => void;
|
||||
}
|
||||
|
||||
export const loadEditSidebarDialog = () => import("./dialog-edit-sidebar");
|
||||
|
||||
export const showEditSidebarDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: EditSidebarDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-edit-sidebar",
|
||||
dialogImport: loadEditSidebarDialog,
|
||||
dialogParams,
|
||||
});
|
||||
};
|
@ -10,6 +10,7 @@ import { showNotificationDrawer } from "../dialogs/notifications/show-notificati
|
||||
import type { HomeAssistant, Route } from "../types";
|
||||
import "./partial-panel-resolver";
|
||||
import { computeRTLDirection } from "../common/util/compute_rtl";
|
||||
import { storage } from "../common/decorators/storage";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
@ -25,7 +26,8 @@ declare global {
|
||||
}
|
||||
|
||||
interface EditSideBarEvent {
|
||||
editMode: boolean;
|
||||
order: string[];
|
||||
hidden: string[];
|
||||
}
|
||||
|
||||
@customElement("home-assistant-main")
|
||||
@ -42,6 +44,22 @@ export class HomeAssistantMain extends LitElement {
|
||||
|
||||
@state() private _drawerOpen = false;
|
||||
|
||||
@state()
|
||||
@storage({
|
||||
key: "sidebarPanelOrder",
|
||||
state: true,
|
||||
subscribe: true,
|
||||
})
|
||||
private _panelOrder: string[] = [];
|
||||
|
||||
@state()
|
||||
@storage({
|
||||
key: "sidebarHiddenPanels",
|
||||
state: true,
|
||||
subscribe: true,
|
||||
})
|
||||
private _hiddenPanels: string[] = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
listenMediaQuery("(max-width: 870px)", (matches) => {
|
||||
@ -63,7 +81,8 @@ export class HomeAssistantMain extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.narrow=${sidebarNarrow}
|
||||
.route=${this.route}
|
||||
.editMode=${this._sidebarEditMode}
|
||||
.panelOrder=${this._panelOrder}
|
||||
.hiddenPanels=${this._hiddenPanels}
|
||||
.alwaysExpand=${sidebarNarrow || this.hass.dockedSidebar === "docked"}
|
||||
></ha-sidebar>
|
||||
<partial-panel-resolver
|
||||
@ -90,17 +109,8 @@ export class HomeAssistantMain extends LitElement {
|
||||
this.addEventListener(
|
||||
"hass-edit-sidebar",
|
||||
(ev: HASSDomEvent<EditSideBarEvent>) => {
|
||||
this._sidebarEditMode = ev.detail.editMode;
|
||||
|
||||
if (this._sidebarEditMode) {
|
||||
if (this._sidebarNarrow) {
|
||||
this._drawerOpen = true;
|
||||
} else {
|
||||
fireEvent(this, "hass-dock-sidebar", {
|
||||
dock: "docked",
|
||||
});
|
||||
}
|
||||
}
|
||||
this._panelOrder = ev.detail.order;
|
||||
this._hiddenPanels = ev.detail.hidden;
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -10,6 +10,7 @@ import { isExternal } from "../../data/external";
|
||||
import type { CoreFrontendUserData } from "../../data/frontend";
|
||||
import { subscribeFrontendUserData } from "../../data/frontend";
|
||||
import { showConfirmationDialog } from "../../dialogs/generic/show-dialog-box";
|
||||
import { showEditSidebarDialog } from "../../dialogs/sidebar/show-dialog-edit-sidebar";
|
||||
import "../../layouts/hass-tabs-subpage";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../types";
|
||||
@ -247,9 +248,18 @@ class HaProfileSectionGeneral extends LitElement {
|
||||
}
|
||||
|
||||
private _customizeSidebar() {
|
||||
fireEvent(this, "hass-edit-sidebar", { editMode: true });
|
||||
showEditSidebarDialog(this, {
|
||||
saveCallback: this._saveSidebar,
|
||||
});
|
||||
}
|
||||
|
||||
private _saveSidebar = (order: string[], hidden: string[]) => {
|
||||
fireEvent(this, "hass-edit-sidebar", {
|
||||
order,
|
||||
hidden,
|
||||
});
|
||||
};
|
||||
|
||||
private _handleLogOut() {
|
||||
showConfirmationDialog(this, {
|
||||
title: this.hass.localize("ui.panel.profile.logout_title"),
|
||||
|
@ -1,73 +0,0 @@
|
||||
import { css } from "lit";
|
||||
|
||||
export const sidebarEditStyle = css`
|
||||
ha-sortable ha-md-list-item.draggable:nth-child(2n) {
|
||||
animation-name: keyframes1;
|
||||
animation-iteration-count: infinite;
|
||||
transform-origin: 50% 10%;
|
||||
animation-delay: -0.75s;
|
||||
animation-duration: 0.25s;
|
||||
}
|
||||
|
||||
ha-sortable ha-md-list-item.draggable:nth-child(2n-1) {
|
||||
animation-name: keyframes2;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
transform-origin: 30% 5%;
|
||||
animation-delay: -0.5s;
|
||||
animation-duration: 0.33s;
|
||||
}
|
||||
|
||||
ha-sortable ha-md-list-item.draggable {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.hidden-panel {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
@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;
|
||||
--mdc-icon-button-size: 24px;
|
||||
}
|
||||
|
||||
:host([expanded]) .hide-panel {
|
||||
display: block;
|
||||
}
|
||||
|
||||
:host([expanded]) .show-panel {
|
||||
display: block;
|
||||
}
|
||||
|
||||
ha-md-list-item.hidden-panel,
|
||||
ha-md-list-item.hidden-panel span,
|
||||
ha-md-list-item.hidden-panel ha-icon[slot="start"] {
|
||||
color: var(--secondary-text-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
@ -2021,9 +2021,7 @@
|
||||
"sidebar": {
|
||||
"external_app_configuration": "App settings",
|
||||
"sidebar_toggle": "Sidebar toggle",
|
||||
"done": "Done",
|
||||
"hide_panel": "Hide panel",
|
||||
"show_panel": "Show panel"
|
||||
"edit_sidebar": "Edit sidebar"
|
||||
},
|
||||
"panel": {
|
||||
"my": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user