Skip to content

Commit

Permalink
Move view to another dashboard (#23245)
Browse files Browse the repository at this point in the history
* Add move view to dashboard

* Remove unneeded return in error flow

* Update src/panels/lovelace/editor/select-dashboard/hui-dialog-select-dashboard.ts

Co-authored-by: Petar Petrov <[email protected]>

* Update src/panels/lovelace/editor/select-dashboard/hui-dialog-select-dashboard.ts

Co-authored-by: Petar Petrov <[email protected]>

* Use ha-list-item for button-menu

---------

Co-authored-by: Petar Petrov <[email protected]>
  • Loading branch information
wendevlin and MindFreeze authored Dec 11, 2024
1 parent f688780 commit e4fc21c
Show file tree
Hide file tree
Showing 5 changed files with 381 additions and 9 deletions.
31 changes: 27 additions & 4 deletions src/panels/lovelace/editor/config-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,21 @@ export const moveCard = (
export const addView = (
hass: HomeAssistant,
config: LovelaceConfig,
viewConfig: LovelaceViewConfig
viewConfig: LovelaceViewConfig,
tolerantPath = false
): LovelaceConfig => {
if (viewConfig.path && config.views.some((v) => v.path === viewConfig.path)) {
throw new Error(
hass.localize("ui.panel.lovelace.editor.edit_view.error_same_url")
);
if (!tolerantPath) {
throw new Error(
hass.localize("ui.panel.lovelace.editor.edit_view.error_same_url")
);
} else {
// add a suffix to the path
viewConfig = {
...viewConfig,
path: `${viewConfig.path}-2`,
};
}
}
return {
...config,
Expand Down Expand Up @@ -240,6 +249,20 @@ export const deleteView = (
views: config.views.filter((_origView, index) => index !== viewIndex),
});

export const moveViewToDashboard = (
hass: HomeAssistant,
fromConfig: LovelaceConfig,
toConfig: LovelaceConfig,
viewIndex: number
): [LovelaceConfig, LovelaceConfig] => {
const view = fromConfig.views[viewIndex];

return [
deleteView(fromConfig, viewIndex),
addView(hass, toConfig, view, true),
];
};

export const addSection = (
config: LovelaceConfig,
viewIndex: number,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import { mdiClose } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, query, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-md-dialog";
import "../../../../components/ha-dialog-header";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-md-select";
import "../../../../components/ha-md-select-option";
import "../../../../components/ha-button";
import "../../../../components/ha-circular-progress";
import type { LovelaceConfig } from "../../../../data/lovelace/config/types";
import type { LovelaceDashboard } from "../../../../data/lovelace/dashboard";
import { fetchDashboards } from "../../../../data/lovelace/dashboard";
import { haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import type { SelectDashboardDialogParams } from "./show-select-dashboard-dialog";
import type { HaMdDialog } from "../../../../components/ha-md-dialog";

@customElement("hui-dialog-select-dashboard")
export class HuiDialogSelectDashboard extends LitElement {
public hass!: HomeAssistant;

@state() private _params?: SelectDashboardDialogParams;

@state() private _dashboards?: LovelaceDashboard[];

@state() private _fromUrlPath?: string | null;

@state() private _toUrlPath?: string | null;

@state() private _config?: LovelaceConfig;

@state() private _saving = false;

@query("ha-md-dialog") private _dialog?: HaMdDialog;

public showDialog(params: SelectDashboardDialogParams): void {
this._config = params.lovelaceConfig;
this._fromUrlPath = params.urlPath;
this._params = params;
this._getDashboards();
}

public closeDialog(): void {
this._saving = false;
this._dashboards = undefined;
this._toUrlPath = undefined;
this._dialog?.close();
}

private _dialogClosed(): void {
this._params = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}

protected render() {
if (!this._params) {
return nothing;
}

const dialogTitle =
this._params.header ||
this.hass.localize("ui.panel.lovelace.editor.select_dashboard.header");

return html`
<ha-md-dialog
open
@closed=${this._dialogClosed}
.ariaLabel=${dialogTitle}
.disableCancelAction=${this._saving}
>
<ha-dialog-header slot="headline">
<ha-icon-button
slot="navigationIcon"
.label=${this.hass.localize("ui.dialogs.generic.close")}
.path=${mdiClose}
@click=${this.closeDialog}
.disabled=${this._saving}
></ha-icon-button>
<span slot="title" .title=${dialogTitle}>${dialogTitle}</span>
</ha-dialog-header>
<div slot="content">
${this._dashboards && !this._saving
? html`
<ha-md-select
.label=${this.hass.localize(
"ui.panel.lovelace.editor.select_view.dashboard_label"
)}
@change=${this._dashboardChanged}
.value=${this._toUrlPath || ""}
>
${this._dashboards.map(
(dashboard) => html`
<ha-md-select-option
.disabled=${dashboard.mode !== "storage" ||
dashboard.url_path === this._fromUrlPath ||
(dashboard.url_path === "lovelace" &&
this._fromUrlPath === null)}
.value=${dashboard.url_path}
>${dashboard.title}</ha-md-select-option
>
`
)}
</ha-md-select>
`
: html`<div class="loading">
<ha-circular-progress
indeterminate
size="medium"
></ha-circular-progress>
</div>`}
</div>
<div slot="actions">
<ha-button @click=${this.closeDialog} .disabled=${this._saving}>
${this.hass!.localize("ui.common.cancel")}
</ha-button>
<ha-button
@click=${this._selectDashboard}
.disabled=${!this._config ||
this._fromUrlPath === this._toUrlPath ||
this._saving}
>
${this._params.actionLabel || this.hass!.localize("ui.common.move")}
</ha-button>
</div>
</ha-md-dialog>
`;
}

private async _getDashboards() {
this._dashboards = [
{
id: "lovelace",
url_path: "lovelace",
require_admin: false,
show_in_sidebar: true,
title: this.hass.localize("ui.common.default"),
mode: this.hass.panels.lovelace?.config?.mode,
},
...(this._params!.dashboards || (await fetchDashboards(this.hass))),
].filter(
(dashboard) => this.hass.user!.is_admin || !dashboard.require_admin
);

const currentPath = this._fromUrlPath || this.hass.defaultPanel;
for (const dashboard of this._dashboards!) {
if (dashboard.url_path !== currentPath) {
this._toUrlPath = dashboard.url_path;
break;
}
}
}

private async _dashboardChanged(ev) {
const urlPath: string = ev.target.value;
if (urlPath === this._toUrlPath) {
return;
}
this._toUrlPath = urlPath;
}

private async _selectDashboard() {
this._saving = true;
if (this._toUrlPath! === "lovelace") {
this._toUrlPath = null;
}
this._params!.dashboardSelectedCallback(this._toUrlPath!);
this.closeDialog();
}

static get styles(): CSSResultGroup {
return [
haStyleDialog,
css`
ha-md-select {
width: 100%;
}
.loading {
display: flex;
justify-content: center;
}
`,
];
}
}

declare global {
interface HTMLElementTagNameMap {
"hui-dialog-select-dashboard": HuiDialogSelectDashboard;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { fireEvent } from "../../../../common/dom/fire_event";
import type { LovelaceConfig } from "../../../../data/lovelace/config/types";
import type { LovelaceDashboard } from "../../../../data/lovelace/dashboard";

export interface SelectDashboardDialogParams {
lovelaceConfig: LovelaceConfig;
dashboards?: LovelaceDashboard[];
urlPath?: string | null;
header?: string;
actionLabel?: string;
dashboardSelectedCallback: (urlPath: string | null) => any;
}

export const showSelectDashboardDialog = (
element: HTMLElement,
selectViewDialogParams: SelectDashboardDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "hui-dialog-select-dashboard",
dialogImport: () => import("./hui-dialog-select-dashboard"),
dialogParams: selectViewDialogParams,
});
};
Loading

0 comments on commit e4fc21c

Please sign in to comment.