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

Adjust schedule helper UI with minute granularity #21073

Merged
merged 4 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/components/ha-selector/ha-selector-time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class HaTimeSelector extends LitElement {
.required=${this.required}
.helper=${this.helper}
.label=${this.label}
enable-second
.enableSecond=${!this.selector.time?.no_second}
></ha-time-input>
`;
}
Expand Down
3 changes: 1 addition & 2 deletions src/data/selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,8 +425,7 @@ export interface ThemeSelector {
theme: { include_default?: boolean } | null;
}
export interface TimeSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
time: {} | null;
time: { no_second?: boolean } | null;
}

export interface TriggerSelector {
Expand Down
133 changes: 133 additions & 0 deletions src/panels/config/helpers/forms/dialog-schedule-block-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { CSSResultGroup, html, LitElement, nothing } from "lit";
import { property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import { createCloseHeading } from "../../../../components/ha-dialog";
import "../../../../components/ha-form/ha-form";
import "../../../../components/ha-button";
import { haStyleDialog } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import {
ScheduleBlockInfo,
ScheduleBlockInfoDialogParams,
} from "./show-dialog-schedule-block-info";
import type { SchemaUnion } from "../../../../components/ha-form/types";

const SCHEMA = [
{
name: "from",
required: true,
selector: { time: { no_second: true } },
},
{
name: "to",
required: true,
selector: { time: { no_second: true } },
},
];

class DialogScheduleBlockInfo extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;

@state() private _error?: Record<string, string>;

@state() private _data?: ScheduleBlockInfo;

@state() private _params?: ScheduleBlockInfoDialogParams;

public showDialog(params: ScheduleBlockInfoDialogParams): void {
this._params = params;
this._error = undefined;
this._data = params.block;
}

public closeDialog(): void {
this._params = undefined;
this._data = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}

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

return html`
<ha-dialog
open
@closed=${this.closeDialog}
.heading=${createCloseHeading(
this.hass,
this.hass!.localize(
karwosts marked this conversation as resolved.
Show resolved Hide resolved
"ui.dialogs.helper_settings.schedule.edit_schedule_block"
)
)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
)}
scrimClickAction
escapeKeyAction
)}

This will prevent it from closing when clicking outside the dialog and using escape to close.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this keeps the edit time popup open on escape, the escape key still dismisses the more-info window underneath it, so I'm not sure if that's an improvement.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parent modal "ha-more-info" then needs both escapeKeyAction and scrimClickAction set to a boolean, depending if the child modal is open. Looking in how this can be done, it's not that easy and straight forward.

Copy link
Contributor Author

@karwosts karwosts Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this is a general preexisting issue, as any dialog from the more-info (like options flow) also allows dismissing the more-info underneath it with Escape. (scrim click seems to work as expected though)

>
<div>
<ha-form
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// offtopic
We should really just add a default form-dialog that you only pass a schema and data 🙃

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had something like this started in one of my changes I previously worked on but never committed... maybe I'll go see if I can find it.

.hass=${this.hass}
.schema=${SCHEMA}
.data=${this._data}
.error=${this._error}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
></ha-form>
</div>
<ha-button
slot="secondaryAction"
class="warning"
@click=${this._deleteBlock}
>
${this.hass!.localize("ui.common.delete")}
</ha-button>
<ha-button slot="primaryAction" @click=${this._updateBlock}>
${this.hass!.localize("ui.common.save")}
</ha-button>
</ha-dialog>
`;
}

private _valueChanged(ev: CustomEvent) {
this._error = undefined;
this._data = ev.detail.value;
}

private _updateBlock() {
try {
this._params!.updateBlock!(this._data!);
this.closeDialog();
} catch (err: any) {
karwosts marked this conversation as resolved.
Show resolved Hide resolved
this._error = { base: err ? err.message : "Unknown error" };
}
karwosts marked this conversation as resolved.
Show resolved Hide resolved
}

private _deleteBlock() {
try {
this._params!.deleteBlock!();
this.closeDialog();
} catch (err: any) {
this._error = { base: err ? err.message : "Unknown error" };
}
karwosts marked this conversation as resolved.
Show resolved Hide resolved
}

private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) => {
switch (schema.name) {
case "from":
return this.hass!.localize("ui.dialogs.helper_settings.schedule.start");
case "to":
return this.hass!.localize("ui.dialogs.helper_settings.schedule.end");
}
return "";
};

static get styles(): CSSResultGroup {
return [haStyleDialog];
}
}

declare global {
interface HTMLElementTagNameMap {
"dialog-schedule-block-info": DialogScheduleBlockInfo;
}
}

customElements.define("dialog-schedule-block-info", DialogScheduleBlockInfo);
41 changes: 27 additions & 14 deletions src/panels/config/helpers/forms/ha-schedule-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import "../../../../components/ha-icon-picker";
import "../../../../components/ha-textfield";
import { Schedule, ScheduleDay, weekdays } from "../../../../data/schedule";
import { TimeZone } from "../../../../data/translation";
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
import { showScheduleBlockInfoDialog } from "./show-dialog-schedule-block-info";
import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";

Expand Down Expand Up @@ -352,21 +352,34 @@ class HaScheduleForm extends LitElement {
}

private async _handleEventClick(info: any) {
if (
!(await showConfirmationDialog(this, {
title: this.hass.localize("ui.dialogs.helper_settings.schedule.delete"),
text: this.hass.localize(
"ui.dialogs.helper_settings.schedule.confirm_delete"
),
destructive: true,
confirmText: this.hass.localize("ui.common.delete"),
}))
) {
return;
}
const [day, index] = info.event.id.split("-");
const value = [...this[`_${day}`]];
const item = [...this[`_${day}`]][index];
showScheduleBlockInfoDialog(this, {
block: item,
updateBlock: (newBlock) => this._updateBlock(day, index, newBlock),
deleteBlock: () => this._deleteBlock(day, index),
});
}

private _updateBlock(day, index, newBlock) {
const [fromH, fromM, _fromS] = newBlock.from.split(":");
newBlock.from = `${fromH}:${fromM}`;
const [toH, toM, _toS] = newBlock.to.split(":");
newBlock.to = `${toH}:${toM}`;
if (Number(toH) === 0 && Number(toM) === 0) {
newBlock.to = "24:00";
}
const newValue = { ...this._item };
newValue[day] = [...this._item![day]];
newValue[day][index] = newBlock;

fireEvent(this, "value-changed", {
value: newValue,
});
}

private _deleteBlock(day, index) {
const value = [...this[`_${day}`]];
const newValue = { ...this._item };
value.splice(parseInt(index), 1);
newValue[day] = value;
Expand Down
26 changes: 26 additions & 0 deletions src/panels/config/helpers/forms/show-dialog-schedule-block-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { fireEvent } from "../../../../common/dom/fire_event";

export interface ScheduleBlockInfo {
from: string;
to: string;
}

export interface ScheduleBlockInfoDialogParams {
block: ScheduleBlockInfo;
updateBlock?: (update: ScheduleBlockInfo) => void;
deleteBlock?: () => void;
}

export const loadScheduleBlockInfoDialog = () =>
import("./dialog-schedule-block-info");

export const showScheduleBlockInfoDialog = (
element: HTMLElement,
params: ScheduleBlockInfoDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-schedule-block-info",
dialogImport: loadScheduleBlockInfoDialog,
dialogParams: params,
});
};
5 changes: 4 additions & 1 deletion src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1510,7 +1510,10 @@
},
"schedule": {
"delete": "Delete item?",
"confirm_delete": "Do you want to delete this item?"
"confirm_delete": "Do you want to delete this item?",
"edit_schedule_block": "Edit schedule block",
"start": "Start",
"end": "End"
},
"template": {
"time": "[%key:ui::panel::developer-tools::tabs::templates::time%]",
Expand Down
Loading