Compare commits

...

6 Commits

Author SHA1 Message Date
Petar Petrov
04bfc350ea Blur view when date picker is open 2025-12-09 15:25:09 +02:00
Petar Petrov
75ec9404ce Sticky period selector for energy dashboard 2025-12-09 13:15:12 +02:00
Aidan Timson
41cabde393 Migrate dialog-download-decrypted-backup to ha-wa-dialog (#28442)
* Migrate dialog-download-decrypted-backup.ts from ha-md-dialog to ha-wa-dialog

* Fixes from other migrations
2025-12-09 09:28:44 +02:00
Aidan Timson
47d1fdf673 Migrate change/show backup encryption key to ha-wa-dialog (#28428)
* Migrate dialog-change-backup-encryption-key.ts from ha-md-dialog to ha-wa-dialog

* Remove open render nothing, remove custom css size

* Migrate show backup key

* Apply suggestion from @MindFreeze

---------

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2025-12-09 07:24:43 +00:00
Aidan Timson
e59e83fffe Migrate dialog-areas-floors-order to ha-wa-dialog (#28424)
* Migrate dialog-areas-floors-order.ts from ha-md-dialog to ha-wa-dialog

* Fix saving

* Remove render nothing if dialog false

* Remove custom width
2025-12-09 09:12:29 +02:00
Aidan Timson
b896b78876 Migrate labs dialogs to ha-wa-dialog (#28429)
* Migrate dialog-labs-preview-feature-enable.ts from ha-md-dialog to ha-wa-dialog

* Migrate dialog-labs-progress.ts from ha-md-dialog to ha-wa-dialog

* Restore

* Remove use of slots

* Fix

* Remove header
2025-12-09 08:55:28 +02:00
14 changed files with 348 additions and 378 deletions

View File

@@ -101,6 +101,10 @@ const Component = Vue.extend({
type: String,
default: "en",
},
opensVertical: {
type: String,
default: undefined,
},
},
render(createElement) {
// @ts-expect-error
@@ -129,6 +133,11 @@ const Component = Vue.extend({
},
expression: "dateRange",
},
on: {
toggle: (open: boolean) => {
fireEvent(this.$el as HTMLElement, "toggle", { open });
},
},
scopedSlots: {
input() {
return createElement("slot", {
@@ -309,6 +318,10 @@ class DateRangePickerElement extends WrappedElement {
min-width: unset !important;
display: block !important;
}
:host([opens-vertical="up"]) .daterangepicker {
bottom: 100%;
top: auto !important;
}
`;
if (mainWindow.document.dir === "rtl") {
style.innerHTML += `
@@ -340,4 +353,7 @@ declare global {
interface HTMLElementTagNameMap {
"date-range-picker": DateRangePickerElement;
}
interface HASSDomEvents {
toggle: { open: boolean };
}
}

View File

@@ -74,6 +74,9 @@ export class HaDateRangePicker extends LitElement {
@property({ attribute: "extended-presets", type: Boolean })
public extendedPresets = false;
@property({ attribute: "vertical-opening-direction" })
public verticalOpeningDirection?: "up" | "down";
@property({ attribute: false }) public openingDirection?:
| "right"
| "left"
@@ -127,6 +130,7 @@ export class HaDateRangePicker extends LitElement {
opening-direction=${ifDefined(
this.openingDirection || this._calcedOpeningDirection
)}
opens-vertical=${ifDefined(this.verticalOpeningDirection)}
first-day=${firstWeekdayIndex(this.hass.locale)}
language=${this.hass.locale.language}
@change=${this._handleChange}

View File

@@ -1,7 +1,7 @@
import { mdiClose, mdiDragHorizontalVariant, mdiTextureBox } from "@mdi/js";
import { mdiDragHorizontalVariant, mdiTextureBox } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import {
type AreasFloorHierarchy,
@@ -11,12 +11,10 @@ import {
} from "../../../common/areas/areas-floor-hierarchy";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-button";
import "../../../components/ha-dialog-header";
import "../../../components/ha-dialog-footer";
import "../../../components/ha-floor-icon";
import "../../../components/ha-icon";
import "../../../components/ha-icon-button";
import "../../../components/ha-md-dialog";
import type { HaMdDialog } from "../../../components/ha-md-dialog";
import "../../../components/ha-wa-dialog";
import "../../../components/ha-md-list";
import "../../../components/ha-md-list-item";
import "../../../components/ha-sortable";
@@ -49,8 +47,6 @@ class DialogAreasFloorsOrder extends LitElement {
@state() private _saving = false;
@query("ha-md-dialog") private _dialog?: HaMdDialog;
public async showDialog(
_params: AreasFloorsOrderDialogParams
): Promise<void> {
@@ -66,7 +62,8 @@ class DialogAreasFloorsOrder extends LitElement {
}
public closeDialog(): void {
this._dialog?.close();
this._saving = false;
this._open = false;
}
private _dialogClosed(): void {
@@ -77,7 +74,7 @@ class DialogAreasFloorsOrder extends LitElement {
}
protected render() {
if (!this._open || !this._hierarchy) {
if (!this._hierarchy) {
return nothing;
}
@@ -89,17 +86,13 @@ class DialogAreasFloorsOrder extends LitElement {
);
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")}
.path=${mdiClose}
@click=${this.closeDialog}
></ha-icon-button>
<span slot="title" .title=${dialogTitle}>${dialogTitle}</span>
</ha-dialog-header>
<div slot="content" class="content">
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
header-title=${dialogTitle}
@closed=${this._dialogClosed}
>
<div class="content">
<ha-sortable
handle-selector=".floor-handle"
draggable-selector=".floor"
@@ -116,15 +109,23 @@ class DialogAreasFloorsOrder extends LitElement {
</ha-sortable>
${this._renderUnassignedAreas()}
</div>
<div slot="actions">
<ha-button @click=${this.closeDialog} appearance="plain">
<ha-dialog-footer slot="footer">
<ha-button
slot="secondaryAction"
@click=${this.closeDialog}
appearance="plain"
>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button @click=${this._save} .disabled=${this._saving}>
<ha-button
slot="primaryAction"
@click=${this._save}
.disabled=${this._saving}
>
${this.hass.localize("ui.common.save")}
</ha-button>
</div>
</ha-md-dialog>
</ha-dialog-footer>
</ha-wa-dialog>
`;
}
@@ -391,15 +392,13 @@ class DialogAreasFloorsOrder extends LitElement {
haStyle,
haStyleDialog,
css`
ha-md-dialog {
min-width: 600px;
ha-wa-dialog {
max-height: 90%;
--dialog-content-padding: 8px 24px;
--dialog-content-padding: var(--ha-space-2) var(--ha-space-6);
}
@media all and (max-width: 600px), all and (max-height: 500px) {
ha-md-dialog {
--md-dialog-container-shape: 0;
@media all and (max-width: 580px), all and (max-height: 500px) {
ha-wa-dialog {
min-width: 100%;
min-height: 100%;
}

View File

@@ -1,15 +1,14 @@
import { mdiClose, mdiContentCopy, mdiDownload } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
import "../../../../components/ha-button";
import "../../../../components/ha-dialog-header";
import "../../../../components/ha-dialog-footer";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-icon-button-prev";
import "../../../../components/ha-md-dialog";
import type { HaMdDialog } from "../../../../components/ha-md-dialog";
import "../../../../components/ha-wa-dialog";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-password-field";
@@ -31,20 +30,18 @@ type Step = (typeof STEPS)[number];
class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _opened = false;
@state() private _open = false;
@state() private _step?: Step;
@state() private _params?: ChangeBackupEncryptionKeyDialogParams;
@query("ha-md-dialog") private _dialog!: HaMdDialog;
@state() private _newEncryptionKey?: string;
public showDialog(params: ChangeBackupEncryptionKeyDialogParams): void {
this._params = params;
this._step = STEPS[0];
this._opened = true;
this._open = true;
this._newEncryptionKey = generateEncryptionKey();
}
@@ -52,10 +49,10 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
if (this._params!.cancel) {
this._params!.cancel();
}
if (this._opened) {
if (this._open) {
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
this._opened = false;
this._open = false;
this._step = undefined;
this._params = undefined;
this._newEncryptionKey = undefined;
@@ -64,7 +61,7 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
private _done() {
this._params?.submit!(true);
this._dialog.close();
this.closeDialog();
}
private _previousStep() {
@@ -84,10 +81,6 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
}
protected render() {
if (!this._opened || !this._params) {
return nothing;
}
const dialogTitle =
this._step === "current" || this._step === "new"
? this.hass.localize(
@@ -96,36 +89,40 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
: "";
return html`
<ha-md-dialog disable-cancel-action open @closed=${this.closeDialog}>
<ha-dialog-header slot="headline">
${this._step === "new"
? html`
<ha-icon-button-prev
slot="navigationIcon"
@click=${this._previousStep}
></ha-icon-button-prev>
`
: html`
<ha-icon-button
slot="navigationIcon"
.label=${this.hass.localize("ui.common.close")}
.path=${mdiClose}
@click=${this.closeDialog}
></ha-icon-button>
`}
<span slot="title">${dialogTitle}</span>
</ha-dialog-header>
<div slot="content">${this._renderStepContent()}</div>
<div slot="actions">
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
header-title=${dialogTitle}
prevent-scrim-close
@closed=${this.closeDialog}
>
${this._step === "new"
? html`
<ha-icon-button-prev
slot="headerNavigationIcon"
@click=${this._previousStep}
></ha-icon-button-prev>
`
: html`
<ha-icon-button
slot="headerNavigationIcon"
data-dialog="close"
.label=${this.hass.localize("ui.common.close")}
.path=${mdiClose}
></ha-icon-button>
`}
${this._renderStepContent()}
<ha-dialog-footer slot="footer">
${this._step === "current"
? html`
<ha-button @click=${this._nextStep}>
<ha-button slot="primaryAction" @click=${this._nextStep}>
${this.hass.localize("ui.common.next")}
</ha-button>
`
: this._step === "new"
? html`
<ha-button
slot="primaryAction"
@click=${this._submit}
.disabled=${!this._newEncryptionKey}
variant="danger"
@@ -136,14 +133,14 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
</ha-button>
`
: html`
<ha-button @click=${this._done}>
<ha-button slot="primaryAction" @click=${this._done}>
${this.hass.localize(
"ui.panel.config.backup.dialogs.change_encryption_key.actions.done"
)}
</ha-button>
`}
</div>
</ha-md-dialog>
</ha-dialog-footer>
</ha-wa-dialog>
`;
}
@@ -287,10 +284,8 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
haStyle,
haStyleDialog,
css`
ha-md-dialog {
width: 90vw;
max-width: 560px;
--dialog-content-padding: 8px 24px;
ha-wa-dialog {
--dialog-content-padding: var(--ha-space-2) var(--ha-space-6);
}
ha-md-list {
background: none;
@@ -321,14 +316,7 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
flex: none;
margin: -16px;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
ha-md-dialog {
max-width: none;
}
div[slot="content"] {
margin-top: 0;
}
}
p {
margin-top: 0;
}

View File

@@ -1,18 +1,12 @@
import { mdiClose } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-dialog-header";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-icon-next";
import "../../../../components/ha-md-dialog";
import type { HaMdDialog } from "../../../../components/ha-md-dialog";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-password-field";
import "../../../../components/ha-alert";
import "../../../../components/ha-button";
import "../../../../components/ha-dialog-footer";
import "../../../../components/ha-wa-dialog";
import "../../../../components/ha-password-field";
import {
canDecryptBackupOnDownload,
getPreferredAgentForDownload,
@@ -27,102 +21,96 @@ import type { DownloadDecryptedBackupDialogParams } from "./show-dialog-download
class DialogDownloadDecryptedBackup extends LitElement implements HassDialog {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _opened = false;
@state() private _open = false;
@state() private _params?: DownloadDecryptedBackupDialogParams;
@query("ha-md-dialog") private _dialog?: HaMdDialog;
@state() private _encryptionKey = "";
@state() private _error = "";
public showDialog(params: DownloadDecryptedBackupDialogParams): void {
this._opened = true;
this._open = true;
this._params = params;
}
public closeDialog() {
this._dialog?.close();
this._open = false;
return true;
}
private _dialogClosed() {
if (this._opened) {
if (this._open) {
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
this._opened = false;
this._open = false;
this._params = undefined;
this._encryptionKey = "";
this._error = "";
}
protected render() {
if (!this._opened || !this._params) {
if (!this._params) {
return nothing;
}
return html`
<ha-md-dialog open @closed=${this._dialogClosed} disable-cancel-action>
<ha-dialog-header slot="headline">
<ha-icon-button
slot="navigationIcon"
@click=${this.closeDialog}
.label=${this.hass.localize("ui.common.close")}
.path=${mdiClose}
></ha-icon-button>
<span slot="title">
${this.hass.localize(
"ui.panel.config.backup.dialogs.download.title"
)}
</span>
</ha-dialog-header>
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
header-title=${this.hass.localize(
"ui.panel.config.backup.dialogs.download.title"
)}
prevent-scrim-close
@closed=${this._dialogClosed}
>
<p>
${this.hass.localize(
"ui.panel.config.backup.dialogs.download.description"
)}
</p>
<p>
${this.hass.localize(
"ui.panel.config.backup.dialogs.download.download_backup_encrypted",
{
download_it_encrypted: html`<button
class="link"
@click=${this._downloadEncrypted}
>
${this.hass.localize(
"ui.panel.config.backup.dialogs.download.download_it_encrypted"
)}
</button>`,
}
)}
</p>
<div slot="content">
<p>
${this.hass.localize(
"ui.panel.config.backup.dialogs.download.description"
)}
</p>
<p>
${this.hass.localize(
"ui.panel.config.backup.dialogs.download.download_backup_encrypted",
{
download_it_encrypted: html`<button
class="link"
@click=${this._downloadEncrypted}
>
${this.hass.localize(
"ui.panel.config.backup.dialogs.download.download_it_encrypted"
)}
</button>`,
}
)}
</p>
<ha-password-field
.label=${this.hass.localize(
"ui.panel.config.backup.dialogs.download.encryption_key"
)}
@input=${this._keyChanged}
></ha-password-field>
<ha-password-field
.label=${this.hass.localize(
"ui.panel.config.backup.dialogs.download.encryption_key"
)}
@input=${this._keyChanged}
></ha-password-field>
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: nothing}
</div>
<div slot="actions">
<ha-button appearance="plain" @click=${this._cancel}>
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: nothing}
<ha-dialog-footer slot="footer">
<ha-button
slot="secondaryAction"
appearance="plain"
@click=${this._cancel}
>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button @click=${this._submit}>
<ha-button slot="primaryAction" @click=${this._submit}>
${this.hass.localize(
"ui.panel.config.backup.dialogs.download.download"
)}
</ha-button>
</div>
</ha-md-dialog>
</ha-dialog-footer>
</ha-wa-dialog>
`;
}
@@ -191,17 +179,8 @@ class DialogDownloadDecryptedBackup extends LitElement implements HassDialog {
haStyle,
haStyleDialog,
css`
ha-md-dialog {
--dialog-content-padding: 8px 24px;
max-width: 500px;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
ha-md-dialog {
max-width: none;
}
div[slot="content"] {
margin-top: 0;
}
ha-wa-dialog {
--dialog-content-padding: var(--ha-space-2) var(--ha-space-6);
}
button.link {

View File

@@ -1,15 +1,14 @@
import { mdiClose, mdiContentCopy, mdiDownload } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
import "../../../../components/ha-button";
import "../../../../components/ha-dialog-header";
import "../../../../components/ha-dialog-footer";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-icon-button-prev";
import "../../../../components/ha-md-dialog";
import type { HaMdDialog } from "../../../../components/ha-md-dialog";
import "../../../../components/ha-wa-dialog";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-password-field";
@@ -24,89 +23,83 @@ import type { ShowBackupEncryptionKeyDialogParams } from "./show-dialog-show-bac
class DialogShowBackupEncryptionKey extends LitElement implements HassDialog {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _params?: ShowBackupEncryptionKeyDialogParams;
@state() private _open = false;
@query("ha-md-dialog") private _dialog!: HaMdDialog;
@state() private _params?: ShowBackupEncryptionKeyDialogParams;
public showDialog(params: ShowBackupEncryptionKeyDialogParams): void {
this._params = params;
this._open = true;
}
public closeDialog() {
if (this._open) {
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
this._open = false;
this._params = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
return true;
}
private _closeDialog() {
this._dialog.close();
}
protected render() {
if (!this._params) {
return nothing;
}
return html`
<ha-md-dialog disable-cancel-action open @closed=${this.closeDialog}>
<ha-dialog-header slot="headline">
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
header-title=${this.hass.localize(
"ui.panel.config.backup.dialogs.show_encryption_key.title"
)}
prevent-scrim-close
@closed=${this.closeDialog}
>
<ha-icon-button
slot="headerNavigationIcon"
data-dialog="close"
.label=${this.hass.localize("ui.common.close")}
.path=${mdiClose}
></ha-icon-button>
<p>
${this.hass.localize(
"ui.panel.config.backup.dialogs.show_encryption_key.description"
)}
</p>
<div class="encryption-key">
<p>${this._params?.currentKey}</p>
<ha-icon-button
slot="navigationIcon"
.label=${this.hass.localize("ui.common.close")}
.path=${mdiClose}
@click=${this._closeDialog}
.path=${mdiContentCopy}
@click=${this._copyKeyToClipboard}
></ha-icon-button>
<span slot="title">
${this.hass.localize(
"ui.panel.config.backup.dialogs.show_encryption_key.title"
)}
</span>
</ha-dialog-header>
<div slot="content">
<p>
${this.hass.localize(
"ui.panel.config.backup.dialogs.show_encryption_key.description"
)}
</p>
<div class="encryption-key">
<p>${this._params?.currentKey}</p>
<ha-icon-button
.path=${mdiContentCopy}
@click=${this._copyKeyToClipboard}
></ha-icon-button>
</div>
<ha-md-list>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.backup.encryption_key.download_emergency_kit"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.backup.encryption_key.download_emergency_kit_description"
)}
</span>
<ha-button
size="small"
appearance="plain"
slot="end"
@click=${this._download}
>
<ha-svg-icon .path=${mdiDownload} slot="start"></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.backup.encryption_key.download_emergency_kit_action"
)}
</ha-button>
</ha-md-list-item>
</ha-md-list>
</div>
<div slot="actions">
<ha-button @click=${this._closeDialog}>
<ha-md-list>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.backup.encryption_key.download_emergency_kit"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.backup.encryption_key.download_emergency_kit_description"
)}
</span>
<ha-button slot="end" @click=${this._download}>
<ha-svg-icon .path=${mdiDownload} slot="start"></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.backup.encryption_key.download_emergency_kit_action"
)}
</ha-button>
</ha-md-list-item>
</ha-md-list>
<ha-dialog-footer slot="footer">
<ha-button slot="primaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.common.close")}
</ha-button>
</div>
</ha-md-dialog>
</ha-dialog-footer>
</ha-wa-dialog>
`;
}
@@ -135,10 +128,8 @@ class DialogShowBackupEncryptionKey extends LitElement implements HassDialog {
haStyle,
haStyleDialog,
css`
ha-md-dialog {
width: 90vw;
max-width: 560px;
--dialog-content-padding: 8px 24px;
ha-wa-dialog {
--dialog-content-padding: var(--ha-space-2) var(--ha-space-6);
}
ha-md-list {
background: none;
@@ -169,14 +160,7 @@ class DialogShowBackupEncryptionKey extends LitElement implements HassDialog {
flex: none;
margin: -16px;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
ha-md-dialog {
max-width: none;
}
div[slot="content"] {
margin-top: 0;
}
}
p {
margin-top: 0;
}

View File

@@ -1,11 +1,11 @@
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { relativeTime } from "../../../common/datetime/relative_time";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-button";
import type { HaMdDialog } from "../../../components/ha-md-dialog";
import "../../../components/ha-md-dialog";
import "../../../components/ha-dialog-footer";
import "../../../components/ha-wa-dialog";
import "../../../components/ha-md-list";
import "../../../components/ha-md-list-item";
import type { HaSwitch } from "../../../components/ha-switch";
@@ -30,13 +30,14 @@ export class DialogLabsPreviewFeatureEnable
@state() private _createBackup = false;
@query("ha-md-dialog") private _dialog?: HaMdDialog;
@state() private _open = false;
public async showDialog(
params: LabsPreviewFeatureEnableDialogParams
): Promise<void> {
this._params = params;
this._createBackup = false;
this._open = true;
this._fetchBackupConfig();
if (isComponentLoaded(this.hass, "hassio")) {
this._fetchUpdateBackupConfig();
@@ -44,7 +45,7 @@ export class DialogLabsPreviewFeatureEnable
}
public closeDialog(): boolean {
this._dialog?.close();
this._open = false;
return true;
}
@@ -143,72 +144,69 @@ export class DialogLabsPreviewFeatureEnable
const createBackupTexts = this._computeCreateBackupTexts();
return html`
<ha-md-dialog open @closed=${this._dialogClosed}>
<span slot="headline">
${this.hass.localize("ui.panel.config.labs.enable_title")}
</span>
<div slot="content">
<p>
${this.hass.localize(
`component.${this._params.preview_feature.domain}.preview_features.${this._params.preview_feature.preview_feature}.enable_confirmation`
) || this.hass.localize("ui.panel.config.labs.enable_confirmation")}
</p>
</div>
<div slot="actions">
${createBackupTexts
? html`
<ha-md-list>
<ha-md-list-item>
<span slot="headline">${createBackupTexts.title}</span>
${createBackupTexts.description
? html`
<span slot="supporting-text">
${createBackupTexts.description}
</span>
`
: nothing}
<ha-switch
slot="end"
.checked=${this._createBackup}
@change=${this._createBackupChanged}
></ha-switch>
</ha-md-list-item>
</ha-md-list>
`
: nothing}
<div>
<ha-button appearance="plain" @click=${this._handleCancel}>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button
appearance="filled"
variant="brand"
@click=${this._handleConfirm}
>
${this.hass.localize("ui.panel.config.labs.enable")}
</ha-button>
</div>
</div>
</ha-md-dialog>
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
header-title=${this.hass.localize("ui.panel.config.labs.enable_title")}
@closed=${this._dialogClosed}
>
<p>
${this.hass.localize(
`component.${this._params.preview_feature.domain}.preview_features.${this._params.preview_feature.preview_feature}.enable_confirmation`
) || this.hass.localize("ui.panel.config.labs.enable_confirmation")}
</p>
${createBackupTexts
? html`
<ha-md-list>
<ha-md-list-item>
<span slot="headline">${createBackupTexts.title}</span>
${createBackupTexts.description
? html`
<span slot="supporting-text">
${createBackupTexts.description}
</span>
`
: nothing}
<ha-switch
slot="end"
.checked=${this._createBackup}
@change=${this._createBackupChanged}
></ha-switch>
</ha-md-list-item>
</ha-md-list>
`
: nothing}
<ha-dialog-footer slot="footer">
<ha-button
slot="secondaryAction"
appearance="plain"
@click=${this._handleCancel}
>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button
slot="primaryAction"
appearance="filled"
variant="brand"
@click=${this._handleConfirm}
>
${this.hass.localize("ui.panel.config.labs.enable")}
</ha-button>
</ha-dialog-footer>
</ha-wa-dialog>
`;
}
static readonly styles = css`
ha-md-dialog {
--dialog-content-padding: var(--ha-space-6);
ha-wa-dialog {
--dialog-content-padding: var(--ha-space-0);
}
p {
margin: 0;
margin: 0 var(--ha-space-6) var(--ha-space-6);
color: var(--secondary-text-color);
}
div[slot="actions"] {
display: flex;
flex-direction: column;
padding: 0;
}
ha-md-list {
background: none;
--md-list-item-leading-space: var(--ha-space-6);
@@ -217,13 +215,6 @@ export class DialogLabsPreviewFeatureEnable
padding: 0;
border-top: var(--ha-border-width-sm) solid var(--divider-color);
}
div[slot="actions"] > div {
display: flex;
justify-content: flex-end;
gap: var(--ha-space-2);
padding: var(--ha-space-4) var(--ha-space-6);
}
`;
}

View File

@@ -1,7 +1,7 @@
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-md-dialog";
import "../../../components/ha-wa-dialog";
import "../../../components/ha-spinner";
import type { HassDialog } from "../../../dialogs/make-dialog-manager";
import type { HomeAssistant } from "../../../types";
@@ -39,36 +39,36 @@ export class DialogLabsProgress
}
return html`
<ha-md-dialog
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
disable-cancel-action
prevent-scrim-close
@closed=${this._handleClosed}
>
<div slot="content">
<div class="summary">
<ha-spinner></ha-spinner>
<div class="content">
<p class="heading">
${this.hass.localize(
"ui.panel.config.labs.progress.creating_backup"
)}
</p>
<p class="description">
${this.hass.localize(
this._params.enabled
? "ui.panel.config.labs.progress.backing_up_before_enabling"
: "ui.panel.config.labs.progress.backing_up_before_disabling"
)}
</p>
</div>
<div slot="header"></div>
<div class="summary">
<ha-spinner></ha-spinner>
<div class="content">
<p class="heading">
${this.hass.localize(
"ui.panel.config.labs.progress.creating_backup"
)}
</p>
<p class="description">
${this.hass.localize(
this._params.enabled
? "ui.panel.config.labs.progress.backing_up_before_enabling"
: "ui.panel.config.labs.progress.backing_up_before_disabling"
)}
</p>
</div>
</div>
</ha-md-dialog>
</ha-wa-dialog>
`;
}
static readonly styles = css`
ha-md-dialog {
ha-wa-dialog {
--dialog-content-padding: var(--ha-space-6);
}

View File

@@ -22,7 +22,10 @@ import {
getSummedData,
} from "../../data/energy";
import type { LovelaceConfig } from "../../data/lovelace/config/types";
import type { LovelaceViewConfig } from "../../data/lovelace/config/view";
import {
isStrategyView,
type LovelaceViewConfig,
} from "../../data/lovelace/config/view";
import type { StatisticValue } from "../../data/recorder";
import { haStyle } from "../../resources/styles";
import type { HomeAssistant, PanelInfo } from "../../types";
@@ -48,6 +51,7 @@ const OVERVIEW_VIEW = {
strategy: {
type: "energy-overview",
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
show_period_selector: true,
},
} as LovelaceViewConfig;
@@ -56,6 +60,7 @@ const ENERGY_VIEW = {
strategy: {
type: "energy",
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
show_period_selector: true,
},
} as LovelaceViewConfig;
@@ -64,6 +69,7 @@ const WATER_VIEW = {
strategy: {
type: "water",
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
show_period_selector: true,
},
} as LovelaceViewConfig;
@@ -72,6 +78,7 @@ const GAS_VIEW = {
strategy: {
type: "gas",
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
show_period_selector: true,
},
} as LovelaceViewConfig;
@@ -223,6 +230,11 @@ class PanelEnergy extends LitElement {
return nothing;
}
const routePath = this.route?.path?.split("/")[1] || "";
const currentView = this._lovelace.config.views.find(
(view) => view.path === routePath
);
return html`
<hui-root
.hass=${this.hass}
@@ -233,6 +245,17 @@ class PanelEnergy extends LitElement {
.extraActionItems=${this._extraActionItems}
@reload-energy-panel=${this._reloadConfig}
></hui-root>
${currentView &&
isStrategyView(currentView) &&
currentView.strategy?.show_period_selector
? html`<ha-card raised class="period-selector">
<hui-energy-period-selector
.hass=${this.hass}
.collectionKey=${DEFAULT_ENERGY_COLLECTION_KEY}
vertical-opening-direction="up"
></hui-energy-period-selector>
</ha-card>`
: nothing}
`;
}
@@ -631,23 +654,8 @@ class PanelEnergy extends LitElement {
-webkit-user-select: none;
-moz-user-select: none;
}
.toolbar {
height: var(--header-height);
display: flex;
flex: 1;
align-items: center;
font-size: var(--ha-font-size-xl);
padding: 0px 12px;
font-weight: var(--ha-font-weight-normal);
box-sizing: border-box;
}
:host([narrow]) .toolbar {
padding: 0 4px;
}
.main-title {
margin: var(--margin-title);
line-height: var(--ha-line-height-normal);
flex-grow: 1;
:host:has([datepicker-open]) hui-root {
filter: blur(4px);
}
.centered {
width: 100%;
@@ -656,6 +664,12 @@ class PanelEnergy extends LitElement {
align-items: center;
justify-content: center;
}
.period-selector {
position: sticky;
bottom: var(--ha-space-2);
margin: 0 auto;
max-width: calc(min(450px, 100% - var(--ha-space-4)));
}
`,
];
}

View File

@@ -5,7 +5,6 @@ import { getEnergyDataCollection } from "../../../data/energy";
import type { HomeAssistant } from "../../../types";
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
@customElement("energy-overview-view-strategy")
@@ -64,24 +63,20 @@ export class EnergyOverviewViewStrategy extends ReactiveElement {
(source.type === "grid" && source.power?.length)
);
const overviewSection: LovelaceSectionConfig = {
type: "grid",
cards: [
{
type: "energy-date-selection",
collection_key: collectionKey,
allow_compare: false,
},
],
};
if (hasGrid || hasBattery || hasSolar) {
overviewSection.cards!.push({
title: hass.localize("ui.panel.energy.cards.energy_distribution_title"),
type: "energy-distribution",
collection_key: collectionKey,
view.sections!.push({
type: "grid",
cards: [
{
title: hass.localize(
"ui.panel.energy.cards.energy_distribution_title"
),
type: "energy-distribution",
collection_key: collectionKey,
},
],
});
}
view.sections!.push(overviewSection);
if (prefs.energy_sources.length) {
view.sections!.push({

View File

@@ -53,10 +53,6 @@ export class EnergyViewStrategy extends ReactiveElement {
(d) => d.included_in_stat
);
view.cards!.push({
type: "energy-date-selection",
collection_key: collectionKey,
});
view.cards!.push({
type: "energy-compare",
collection_key: "energy_dashboard",

View File

@@ -40,10 +40,6 @@ export class GasViewStrategy extends ReactiveElement {
const section = view.sections![0] as LovelaceSectionConfig;
section.cards!.push({
type: "energy-date-selection",
collection_key: collectionKey,
});
section.cards!.push({
type: "energy-compare",
collection_key: collectionKey,

View File

@@ -41,10 +41,6 @@ export class WaterViewStrategy extends ReactiveElement {
const section = view.sections![0] as LovelaceSectionConfig;
section.cards!.push({
type: "energy-date-selection",
collection_key: collectionKey,
});
section.cards!.push({
type: "energy-compare",
collection_key: collectionKey,

View File

@@ -69,6 +69,12 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
@property({ type: Boolean, attribute: "allow-compare" }) public allowCompare =
true;
@property({ attribute: "vertical-opening-direction" })
public verticalOpeningDirection?: "up" | "down";
@property({ type: Boolean, reflect: true, attribute: "datepicker-open" })
public datepickerOpen = false;
@state() _startDate?: Date;
@state() _endDate?: Date;
@@ -202,8 +208,10 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
.ranges=${this._ranges}
@value-changed=${this._dateRangeChanged}
@preset-selected=${this._presetSelected}
@toggle=${this._handleDatepickerToggle}
minimal
header-position
.verticalOpeningDirection=${this.verticalOpeningDirection}
></ha-date-range-picker>
</div>
@@ -446,6 +454,10 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
this._endDate = energyData.end || endOfToday();
}
private _handleDatepickerToggle(ev: CustomEvent<{ open: boolean }>) {
this.datepickerOpen = ev.detail.open;
}
private _toggleCompare(ev: CustomEvent<RequestSelectedDetail>) {
if (ev.detail.source !== "interaction") {
return;