Skip to content

Commit

Permalink
First draft of strategy editor
Browse files Browse the repository at this point in the history
  • Loading branch information
piitaya committed Sep 13, 2023
1 parent b950f99 commit cc0a674
Show file tree
Hide file tree
Showing 9 changed files with 307 additions and 11 deletions.
10 changes: 6 additions & 4 deletions src/data/lovelace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ export interface LovelacePanelConfig {
mode: "yaml" | "storage";
}

export interface LovelaceStrategyConfig {
type: string;
options?: Record<string, unknown>;
}

export interface LovelaceConfig {
title?: string;
strategy?: {
type: string;
options?: Record<string, unknown>;
};
strategy?: LovelaceStrategyConfig;
views: LovelaceViewConfig[];
background?: string;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { customElement } from "lit/decorators";
import { LovelaceConfig } from "../../../../data/lovelace";
import {
getLovelaceStrategy,
LovelaceDashboardStrategy,
type LovelaceDashboardStrategyEditor,
} from "../../strategies/get-strategy";
import { HuiElementEditor } from "../hui-element-editor";

@customElement("hui-dashboard-strategy-element-editor")
export class HuiDashboardStrategyElementEditor extends HuiElementEditor<LovelaceConfig> {
protected async getConfigElement(): Promise<
LovelaceDashboardStrategyEditor | undefined
> {
const elClass = await getLovelaceStrategy<LovelaceDashboardStrategy>(
this.configElementType!
);

// Check if a GUI editor exists
if (elClass && elClass.getConfigElement) {
return elClass.getConfigElement();
}

return undefined;
}
}

declare global {
interface HTMLElementTagNameMap {
"hui-dashboard-strategy-element-editor": HuiDashboardStrategyElementEditor;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-form/ha-form";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../components/ha-form/types";
import { LovelaceStrategyConfig } from "../../../../data/lovelace";
import type { HomeAssistant } from "../../../../types";
import { LovelaceDashboardStrategyEditor } from "../../strategies/get-strategy";

const SCHEMA = [
{
name: "no_area_group",
selector: {
boolean: {},
},
},
] as const satisfies readonly HaFormSchema[];

@customElement("hui-original-states-dashboard-strategy-editor")
export class HuiSensorCardEditor
extends LitElement
implements LovelaceDashboardStrategyEditor
{
@property({ attribute: false }) public hass?: HomeAssistant;

@state() private _config?: LovelaceStrategyConfig;

public setConfig(config: LovelaceStrategyConfig): void {
this._config = config;
}

protected render() {
if (!this.hass || !this._config) {
return nothing;
}

const data = this._config.options;

return html`
<ha-form
.hass=${this.hass}
.data=${data}
.schema=${SCHEMA}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
></ha-form>
`;
}

private _valueChanged(ev: CustomEvent): void {
const config = {
type: this._config!.type,
options: ev.detail.value,
};
fireEvent(this, "config-changed", { config });
}

private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) => {
switch (schema.name) {
case "no_area_group":
return "Do not group by area";
default:
return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
);
}
};
}

declare global {
interface HTMLElementTagNameMap {
"hui-original-states-dashboard-strategy-editor": HuiSensorCardEditor;
}
}
4 changes: 3 additions & 1 deletion src/panels/lovelace/editor/hui-element-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type { HaCodeEditor } from "../../../components/ha-code-editor";
import type {
LovelaceCardConfig,
LovelaceConfig,
LovelaceStrategyConfig,
} from "../../../data/lovelace";
import type { HomeAssistant } from "../../../types";
import type { LovelaceRowConfig } from "../entity-rows/types";
Expand All @@ -38,7 +39,8 @@ export interface ConfigChangedEvent {
| LovelaceCardConfig
| LovelaceRowConfig
| LovelaceHeaderFooterConfig
| LovelaceTileFeatureConfig;
| LovelaceTileFeatureConfig
| LovelaceStrategyConfig;
error?: string;
guiModeAvailable?: boolean;
}
Expand Down
13 changes: 11 additions & 2 deletions src/panels/lovelace/hui-root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import {
import "@polymer/paper-tabs/paper-tab";
import "@polymer/paper-tabs/paper-tabs";
import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
TemplateResult,
css,
html,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
Expand Down Expand Up @@ -68,6 +68,7 @@ import { documentationUrl } from "../../util/documentation-url";
import { swapView } from "./editor/config-util";
import { showEditLovelaceDialog } from "./editor/lovelace-editor/show-edit-lovelace-dialog";
import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog";
import { showDashboardStrategyEditorDialog } from "./strategies/device-registry-detail/show-dialog-dashboard-strategy-editor";
import type { Lovelace } from "./types";
import "./views/hui-view";
import type { HUIView } from "./views/hui-view";
Expand Down Expand Up @@ -806,6 +807,14 @@ class HUIRoot extends LitElement {
});
return;
}
if (this.lovelace!.rawConfig?.strategy != null) {
showDashboardStrategyEditorDialog(this, {
lovelaceConfig: this.lovelace!.rawConfig,
saveConfig: this.lovelace!.saveConfig,
strategyConfig: this.lovelace!.rawConfig.strategy,
});
return;
}
this.lovelace!.setEditMode(true);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
import { createCloseHeading } from "../../../../components/ha-dialog";
import { LovelaceStrategyConfig } from "../../../../data/lovelace";
import { haStyle, haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import { showSaveSuccessToast } from "../../../../util/toast-saved-success";
import "../../editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor";
import type { HuiDashboardStrategyElementEditor } from "../../editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor";
import { ConfigChangedEvent } from "../../editor/hui-element-editor";
import { GUIModeChangedEvent } from "../../editor/types";
import type { DashboardStrategyEditorDialogParams } from "./show-dialog-dashboard-strategy-editor";

@customElement("dialog-dashboard-strategy-editor")
class DialogDashboardStrategyEditor extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;

@state() private _params?: DashboardStrategyEditorDialogParams;

@state() private _strategyConfig?: LovelaceStrategyConfig;

@state() private _GUImode = true;

@state() private _guiModeAvailable? = true;

@query("hui-dashboard-strategy-element-editor")
private _strategyEditorEl?: HuiDashboardStrategyElementEditor;

public async showDialog(
params: DashboardStrategyEditorDialogParams
): Promise<void> {
this._params = params;
this._strategyConfig = params.strategyConfig;
await this.updateComplete;
}

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

private _handleConfigChanged(ev: HASSDomEvent<ConfigChangedEvent>) {
ev.stopPropagation();
this._guiModeAvailable = ev.detail.guiModeAvailable;
this._strategyConfig = ev.detail.config as LovelaceStrategyConfig;
}

private _handleGUIModeChanged(ev: HASSDomEvent<GUIModeChangedEvent>): void {
ev.stopPropagation();
this._GUImode = ev.detail.guiMode;
this._guiModeAvailable = ev.detail.guiModeAvailable;
}

private _toggleMode(): void {
this._strategyEditorEl?.toggleMode();
}

private _opened() {
this._strategyEditorEl?.focusYamlEditor();
}

private async _save(): Promise<void> {
await this._params!.saveConfig({
...this._params!.lovelaceConfig,
strategy: this._strategyConfig,
});
showSaveSuccessToast(this, this.hass);
this.closeDialog();
}

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

return html`
<ha-dialog
open
@closed=${this.closeDialog}
.heading=${createCloseHeading(this.hass, "Edit dashboard")}
@opened=${this._opened}
>
<hui-dashboard-strategy-element-editor
.hass=${this.hass}
.lovelace=${this._params.lovelaceConfig}
.value=${this._strategyConfig}
@config-changed=${this._handleConfigChanged}
@GUImode-changed=${this._handleGUIModeChanged}
dialogInitialFocus
></hui-dashboard-strategy-element-editor>
${this._strategyConfig !== undefined
? html`
<mwc-button
slot="secondaryAction"
@click=${this._toggleMode}
.disabled=${!this._guiModeAvailable}
class="gui-mode-button"
>
${this.hass!.localize(
!this._strategyEditorEl || this._GUImode
? "ui.panel.lovelace.editor.edit_card.show_code_editor"
: "ui.panel.lovelace.editor.edit_card.show_visual_editor"
)}
</mwc-button>
<mwc-button @click=${this._save} slot="primaryAction">
${this.hass!.localize("ui.common.save")}
</mwc-button>
`
: nothing}
</ha-dialog>
`;
}

static get styles(): CSSResultGroup {
return [haStyle, haStyleDialog, css``];
}
}

declare global {
interface HTMLElementTagNameMap {
"dialog-dashboard-strategy-editor": DialogDashboardStrategyEditor;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { fireEvent } from "../../../../common/dom/fire_event";
import {
LovelaceConfig,
LovelaceStrategyConfig,
} from "../../../../data/lovelace";

export interface DashboardStrategyEditorDialogParams {
lovelaceConfig: LovelaceConfig;
saveConfig: (config: LovelaceConfig) => void;
strategyConfig: LovelaceStrategyConfig;
}

export const loadDashboardStrategyEditorDialog = () =>
import("./dialog-dashboard-strategy-editor");

export const showDashboardStrategyEditorDialog = (
element: HTMLElement,
params: DashboardStrategyEditorDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-dashboard-strategy-editor",
dialogImport: loadDashboardStrategyEditorDialog,
dialogParams: params,
});
};
15 changes: 13 additions & 2 deletions src/panels/lovelace/strategies/get-strategy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { LovelaceConfig, LovelaceViewConfig } from "../../../data/lovelace";
import {
LovelaceConfig,
LovelaceStrategyConfig,
LovelaceViewConfig,
} from "../../../data/lovelace";
import { AsyncReturnType, HomeAssistant } from "../../../types";
import { LovelaceGenericElementEditor } from "../types";

const MAX_WAIT_STRATEGY_LOAD = 5000;
const CUSTOM_PREFIX = "custom:";
Expand All @@ -10,6 +15,7 @@ export interface LovelaceDashboardStrategy {
hass: HomeAssistant;
narrow: boolean | undefined;
}): Promise<LovelaceConfig>;
getConfigElement?: () => LovelaceDashboardStrategyEditor;
}

export interface LovelaceViewStrategy {
Expand All @@ -21,6 +27,11 @@ export interface LovelaceViewStrategy {
}): Promise<LovelaceViewConfig>;
}

export interface LovelaceDashboardStrategyEditor
extends LovelaceGenericElementEditor {
setConfig(config: LovelaceStrategyConfig): void;
}

const strategies: Record<
string,
() => Promise<LovelaceDashboardStrategy | LovelaceViewStrategy>
Expand All @@ -31,7 +42,7 @@ const strategies: Record<
(await import("../../energy/strategies/energy-strategy")).EnergyStrategy,
};

const getLovelaceStrategy = async <
export const getLovelaceStrategy = async <
T extends LovelaceDashboardStrategy | LovelaceViewStrategy,
>(
strategyType: string
Expand Down
Loading

0 comments on commit cc0a674

Please sign in to comment.