Skip to content

Commit

Permalink
Location/zone editor updates (#19994)
Browse files Browse the repository at this point in the history
  • Loading branch information
karwosts authored Apr 12, 2024
1 parent 178feb7 commit b82f112
Show file tree
Hide file tree
Showing 9 changed files with 308 additions and 67 deletions.
86 changes: 84 additions & 2 deletions src/components/ha-selector/ha-selector-location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import type {
LocationSelectorValue,
} from "../../data/selector";
import type { HomeAssistant } from "../../types";
import type { SchemaUnion } from "../ha-form/types";
import type { MarkerLocation } from "../map/ha-locations-editor";
import "../map/ha-locations-editor";
import "../ha-form/ha-form";

@customElement("ha-selector-location")
export class HaLocationSelector extends LitElement {
Expand All @@ -24,6 +26,49 @@ export class HaLocationSelector extends LitElement {

@property({ type: Boolean, reflect: true }) public disabled = false;

private _schema = memoizeOne(
(radius?: boolean, radius_readonly?: boolean) =>
[
{
name: "",
type: "grid",
schema: [
{
name: "latitude",
required: true,
selector: { number: { step: "any" } },
},
{
name: "longitude",
required: true,
selector: { number: { step: "any" } },
},
],
},
...(radius
? [
{
name: "radius",
required: true,
default: 1000,
disabled: !!radius_readonly,
selector: { number: { min: 0, step: 1, mode: "box" } as const },
} as const,
]
: []),
] as const
);

protected willUpdate() {
if (!this.value) {
this.value = {
latitude: this.hass.config.latitude,
longitude: this.hass.config.longitude,
radius: this.selector.location?.radius ? 1000 : undefined,
};
}
}

protected render() {
return html`
<p>${this.label ? this.label : ""}</p>
Expand All @@ -35,6 +80,17 @@ export class HaLocationSelector extends LitElement {
@location-updated=${this._locationChanged}
@radius-updated=${this._radiusChanged}
></ha-locations-editor>
<ha-form
.hass=${this.hass}
.schema=${this._schema(
this.selector.location?.radius,
this.selector.location?.radius_readonly
)}
.data=${this.value}
.computeLabel=${this._computeLabel}
.disabled=${this.disabled}
@value-changed=${this._valueChanged}
></ha-form>
`;
}

Expand Down Expand Up @@ -66,7 +122,8 @@ export class HaLocationSelector extends LitElement {
? "mdi:map-marker-radius"
: "mdi:map-marker",
location_editable: true,
radius_editable: true,
radius_editable:
!!selector.location?.radius && !selector.location?.radius_readonly,
},
];
}
Expand All @@ -80,14 +137,39 @@ export class HaLocationSelector extends LitElement {
}

private _radiusChanged(ev: CustomEvent) {
const radius = ev.detail.radius;
const radius = Math.round(ev.detail.radius);
fireEvent(this, "value-changed", { value: { ...this.value, radius } });
}

private _valueChanged(ev: CustomEvent) {
ev.stopPropagation();
const value = ev.detail.value;
const radius = Math.round(ev.detail.value.radius);

fireEvent(this, "value-changed", {
value: {
latitude: value.latitude,
longitude: value.longitude,
...(this.selector.location?.radius &&
!this.selector.location?.radius_readonly
? {
radius,
}
: {}),
},
});
}

private _computeLabel = (
entry: SchemaUnion<ReturnType<typeof this._schema>>
): string =>
this.hass.localize(`ui.components.selectors.location.${entry.name}`);

static styles = css`
ha-locations-editor {
display: block;
height: 400px;
margin-bottom: 16px;
}
p {
margin-top: 0;
Expand Down
6 changes: 5 additions & 1 deletion src/data/selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,11 @@ export interface LanguageSelector {
}

export interface LocationSelector {
location: { radius?: boolean; icon?: string } | null;
location: {
radius?: boolean;
radius_readonly?: boolean;
icon?: string;
} | null;
}

export interface LocationSelectorValue {
Expand Down
5 changes: 5 additions & 0 deletions src/data/zone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export interface Zone {
radius?: number;
}

export interface HomeZoneMutableParams {
latitude: number;
longitude: number;
}

export interface ZoneMutableParams {
name: string;
icon?: string;
Expand Down
40 changes: 17 additions & 23 deletions src/panels/config/core/ha-config-section-general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,20 @@ import "../../../components/ha-language-picker";
import "../../../components/ha-radio";
import type { HaRadio } from "../../../components/ha-radio";
import "../../../components/ha-select";
import "../../../components/ha-selector/ha-selector-location";
import type { LocationSelectorValue } from "../../../data/selector";
import "../../../components/ha-settings-row";
import "../../../components/ha-textfield";
import type { HaTextField } from "../../../components/ha-textfield";
import "../../../components/ha-timezone-picker";
import "../../../components/map/ha-locations-editor";
import type { MarkerLocation } from "../../../components/map/ha-locations-editor";
import { ConfigUpdateValues, saveCoreConfig } from "../../../data/core";
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/hass-subpage";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant, ValueChangedEvent } from "../../../types";

const LOCATION_SELECTOR = { location: {} };

@customElement("ha-config-section-general")
class HaConfigSectionGeneral extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
Expand Down Expand Up @@ -244,15 +246,16 @@ class HaConfigSectionGeneral extends LitElement {
</div>
${this.narrow
? html`
<ha-locations-editor
<ha-selector-location
.hass=${this.hass}
.locations=${this._markerLocation(
.selector=${LOCATION_SELECTOR}
.value=${this._selectorLocation(
this.hass.config.latitude,
this.hass.config.longitude,
this._location
)}
@location-updated=${this._locationChanged}
></ha-locations-editor>
@value-changed=${this._locationChanged}
></ha-selector-location>
`
: html`
<ha-settings-row>
Expand Down Expand Up @@ -320,7 +323,7 @@ class HaConfigSectionGeneral extends LitElement {
}

private _locationChanged(ev: CustomEvent) {
this._location = ev.detail.location;
this._location = [ev.detail.value.latitude, ev.detail.value.longitude];
}

private async _updateEntry(ev: CustomEvent) {
Expand Down Expand Up @@ -381,19 +384,15 @@ class HaConfigSectionGeneral extends LitElement {
}
}

private _markerLocation = memoizeOne(
private _selectorLocation = memoizeOne(
(
lat: number,
lng: number,
latDefault: number,
lngDefault: number,
location?: [number, number]
): MarkerLocation[] => [
{
id: "location",
latitude: location ? location[0] : lat,
longitude: location ? location[1] : lng,
location_editable: true,
},
]
): LocationSelectorValue => ({
latitude: location != null ? location[0] : latDefault,
longitude: location != null ? location[1] : lngDefault,
})
);

private _editLocation() {
Expand Down Expand Up @@ -441,11 +440,6 @@ class HaConfigSectionGeneral extends LitElement {
margin-top: 8px;
display: inline-block;
}
ha-locations-editor {
display: block;
height: 400px;
padding: 16px;
}
`,
];
}
Expand Down
150 changes: 150 additions & 0 deletions src/panels/config/zone/dialog-home-zone-detail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import "@material/mwc-button";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import { createCloseHeading } from "../../../components/ha-dialog";
import "../../../components/ha-form/ha-form";
import { HomeZoneMutableParams } from "../../../data/zone";
import { haStyleDialog } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import { HomeZoneDetailDialogParams } from "./show-dialog-home-zone-detail";

const SCHEMA = [
{
name: "location",
required: true,
selector: { location: { radius: true, radius_readonly: true } },
},
];

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

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

@state() private _data?: HomeZoneMutableParams;

@state() private _params?: HomeZoneDetailDialogParams;

@state() private _submitting = false;

public showDialog(params: HomeZoneDetailDialogParams): void {
this._params = params;
this._error = undefined;
this._data = {
latitude: this.hass.config.latitude,
longitude: this.hass.config.longitude,
};
}

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;
}
const latInvalid = String(this._data.latitude) === "";
const lngInvalid = String(this._data.longitude) === "";

const valid = !latInvalid && !lngInvalid;

return html`
<ha-dialog
open
@closed=${this.closeDialog}
scrimClickAction
escapeKeyAction
.heading=${createCloseHeading(
this.hass,
this.hass!.localize("ui.panel.config.zone.edit_home")
)}
>
<div>
<ha-form
.hass=${this.hass}
.schema=${SCHEMA}
.data=${this._formData(this._data)}
.error=${this._error}
.computeLabel=${this._computeLabel}
@value-changed=${this._valueChanged}
></ha-form>
<p>
${this.hass!.localize(
"ui.panel.config.zone.detail.no_edit_home_zone_radius"
)}
</p>
</div>
<mwc-button
slot="primaryAction"
@click=${this._updateEntry}
.disabled=${!valid || this._submitting}
>
${this.hass!.localize("ui.panel.config.zone.detail.update")}
</mwc-button>
</ha-dialog>
`;
}

private _formData = memoizeOne((data: HomeZoneMutableParams) => ({
...data,
location: {
latitude: data.latitude,
longitude: data.longitude,
radius: this.hass.states["zone.home"]?.attributes?.radius || 100,
},
}));

private _valueChanged(ev: CustomEvent) {
this._error = undefined;
const value = { ...ev.detail.value };
value.latitude = value.location.latitude;
value.longitude = value.location.longitude;
delete value.location;
this._data = value;
}

private _computeLabel = (): string => "";

private async _updateEntry() {
this._submitting = true;
try {
await this._params!.updateEntry!(this._data!);
this.closeDialog();
} catch (err: any) {
this._error = { base: err ? err.message : "Unknown error" };
} finally {
this._submitting = false;
}
}

static get styles(): CSSResultGroup {
return [
haStyleDialog,
css`
ha-dialog {
--mdc-dialog-min-width: min(600px, 95vw);
}
@media all and (max-width: 450px), all and (max-height: 500px) {
ha-dialog {
--mdc-dialog-min-width: calc(
100vw - env(safe-area-inset-right) - env(safe-area-inset-left)
);
}
}
`,
];
}
}

declare global {
interface HTMLElementTagNameMap {
"dialog-home-zone-detail": DialogHomeZoneDetail;
}
}

customElements.define("dialog-home-zone-detail", DialogHomeZoneDetail);
Loading

0 comments on commit b82f112

Please sign in to comment.