mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 07:16:39 +00:00
Entities Card: Add Header & Footer Editor (#6751)
This commit is contained in:
parent
8feae04281
commit
32ff166a74
@ -16,6 +16,7 @@ import {
|
|||||||
LovelaceCard,
|
LovelaceCard,
|
||||||
LovelaceCardConstructor,
|
LovelaceCardConstructor,
|
||||||
LovelaceHeaderFooter,
|
LovelaceHeaderFooter,
|
||||||
|
LovelaceHeaderFooterConstructor,
|
||||||
LovelaceRowConstructor,
|
LovelaceRowConstructor,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ interface CreateElementConfigTypes {
|
|||||||
"header-footer": {
|
"header-footer": {
|
||||||
config: LovelaceHeaderFooterConfig;
|
config: LovelaceHeaderFooterConfig;
|
||||||
element: LovelaceHeaderFooter;
|
element: LovelaceHeaderFooter;
|
||||||
constructor: unknown;
|
constructor: LovelaceHeaderFooterConstructor;
|
||||||
};
|
};
|
||||||
view: {
|
view: {
|
||||||
config: LovelaceViewConfig;
|
config: LovelaceViewConfig;
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { LovelaceHeaderFooterConfig } from "../header-footer/types";
|
import { LovelaceHeaderFooterConfig } from "../header-footer/types";
|
||||||
import { createLovelaceElement } from "./create-element-base";
|
import {
|
||||||
|
createLovelaceElement,
|
||||||
|
getLovelaceElementClass,
|
||||||
|
} from "./create-element-base";
|
||||||
|
|
||||||
const LAZY_LOAD_TYPES = {
|
const LAZY_LOAD_TYPES = {
|
||||||
picture: () => import("../header-footer/hui-picture-header-footer"),
|
picture: () => import("../header-footer/hui-picture-header-footer"),
|
||||||
@ -16,3 +19,6 @@ export const createHeaderFooterElement = (config: LovelaceHeaderFooterConfig) =>
|
|||||||
undefined,
|
undefined,
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getHeaderFooterElementClass = (type: string) =>
|
||||||
|
getLovelaceElementClass(type, "header-footer", undefined, LAZY_LOAD_TYPES);
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
import { customElement } from "lit-element";
|
||||||
|
import type { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||||
|
import { getCardElementClass } from "../../create-element/create-card-element";
|
||||||
|
import type { LovelaceCardEditor } from "../../types";
|
||||||
|
import { HuiElementEditor } from "../hui-element-editor";
|
||||||
|
|
||||||
|
@customElement("hui-card-element-editor")
|
||||||
|
export class HuiCardElementEditor extends HuiElementEditor<LovelaceCardConfig> {
|
||||||
|
protected async getConfigElement(): Promise<LovelaceCardEditor | undefined> {
|
||||||
|
const elClass = await getCardElementClass(this.configElementType!);
|
||||||
|
|
||||||
|
// Check if a GUI editor exists
|
||||||
|
if (elClass && elClass.getConfigElement) {
|
||||||
|
return elClass.getConfigElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-card-element-editor": HuiCardElementEditor;
|
||||||
|
}
|
||||||
|
}
|
@ -29,12 +29,10 @@ import type { HomeAssistant } from "../../../../types";
|
|||||||
import { showSaveSuccessToast } from "../../../../util/toast-saved-success";
|
import { showSaveSuccessToast } from "../../../../util/toast-saved-success";
|
||||||
import { addCard, replaceCard } from "../config-util";
|
import { addCard, replaceCard } from "../config-util";
|
||||||
import { getCardDocumentationURL } from "../get-card-documentation-url";
|
import { getCardDocumentationURL } from "../get-card-documentation-url";
|
||||||
import "../hui-element-editor";
|
import type { ConfigChangedEvent } from "../hui-element-editor";
|
||||||
import type {
|
|
||||||
ConfigChangedEvent,
|
|
||||||
HuiElementEditor,
|
|
||||||
} from "../hui-element-editor";
|
|
||||||
import type { GUIModeChangedEvent } from "../types";
|
import type { GUIModeChangedEvent } from "../types";
|
||||||
|
import "./hui-card-element-editor";
|
||||||
|
import type { HuiCardElementEditor } from "./hui-card-element-editor";
|
||||||
import "./hui-card-preview";
|
import "./hui-card-preview";
|
||||||
import type { EditCardDialogParams } from "./show-edit-card-dialog";
|
import type { EditCardDialogParams } from "./show-edit-card-dialog";
|
||||||
|
|
||||||
@ -68,7 +66,8 @@ export class HuiDialogEditCard extends LitElement
|
|||||||
|
|
||||||
@internalProperty() private _guiModeAvailable? = true;
|
@internalProperty() private _guiModeAvailable? = true;
|
||||||
|
|
||||||
@query("hui-element-editor") private _cardEditorEl?: HuiElementEditor;
|
@query("hui-card-element-editor")
|
||||||
|
private _cardEditorEl?: HuiCardElementEditor;
|
||||||
|
|
||||||
@internalProperty() private _GUImode = true;
|
@internalProperty() private _GUImode = true;
|
||||||
|
|
||||||
@ -187,14 +186,14 @@ export class HuiDialogEditCard extends LitElement
|
|||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="element-editor">
|
<div class="element-editor">
|
||||||
<hui-element-editor
|
<hui-card-element-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.lovelace=${this._params.lovelaceConfig}
|
.lovelace=${this._params.lovelaceConfig}
|
||||||
.value=${this._cardConfig}
|
.value=${this._cardConfig}
|
||||||
@config-changed=${this._handleConfigChanged}
|
@config-changed=${this._handleConfigChanged}
|
||||||
@GUImode-changed=${this._handleGUIModeChanged}
|
@GUImode-changed=${this._handleGUIModeChanged}
|
||||||
@editor-save=${this._save}
|
@editor-save=${this._save}
|
||||||
></hui-element-editor>
|
></hui-card-element-editor>
|
||||||
</div>
|
</div>
|
||||||
<div class="element-preview">
|
<div class="element-preview">
|
||||||
<hui-card-preview
|
<hui-card-preview
|
||||||
|
@ -18,12 +18,11 @@ import { LovelaceCardConfig, LovelaceConfig } from "../../../../data/lovelace";
|
|||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { ConditionalCardConfig } from "../../cards/types";
|
import { ConditionalCardConfig } from "../../cards/types";
|
||||||
import { LovelaceCardEditor } from "../../types";
|
import { LovelaceCardEditor } from "../../types";
|
||||||
|
import "../card-editor/hui-card-element-editor";
|
||||||
|
import type { HuiCardElementEditor } from "../card-editor/hui-card-element-editor";
|
||||||
import "../card-editor/hui-card-picker";
|
import "../card-editor/hui-card-picker";
|
||||||
import "../hui-element-editor";
|
import "../hui-element-editor";
|
||||||
import type {
|
import type { ConfigChangedEvent } from "../hui-element-editor";
|
||||||
ConfigChangedEvent,
|
|
||||||
HuiElementEditor,
|
|
||||||
} from "../hui-element-editor";
|
|
||||||
import { GUIModeChangedEvent } from "../types";
|
import { GUIModeChangedEvent } from "../types";
|
||||||
import { configElementStyle } from "./config-elements-style";
|
import { configElementStyle } from "./config-elements-style";
|
||||||
|
|
||||||
@ -53,7 +52,8 @@ export class HuiConditionalCardEditor extends LitElement
|
|||||||
|
|
||||||
@internalProperty() private _cardTab = false;
|
@internalProperty() private _cardTab = false;
|
||||||
|
|
||||||
@query("hui-element-editor") private _cardEditorEl?: HuiElementEditor;
|
@query("hui-card-element-editor")
|
||||||
|
private _cardEditorEl?: HuiCardElementEditor;
|
||||||
|
|
||||||
public setConfig(config: ConditionalCardConfig): void {
|
public setConfig(config: ConditionalCardConfig): void {
|
||||||
assert(config, cardConfigStruct);
|
assert(config, cardConfigStruct);
|
||||||
@ -108,13 +108,13 @@ export class HuiConditionalCardEditor extends LitElement
|
|||||||
)}</mwc-button
|
)}</mwc-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<hui-element-editor
|
<hui-card-element-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._config.card}
|
.value=${this._config.card}
|
||||||
.lovelace=${this.lovelace}
|
.lovelace=${this.lovelace}
|
||||||
@config-changed=${this._handleCardChanged}
|
@config-changed=${this._handleCardChanged}
|
||||||
@GUImode-changed=${this._handleGUIModeChanged}
|
@GUImode-changed=${this._handleGUIModeChanged}
|
||||||
></hui-element-editor>
|
></hui-card-element-editor>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<hui-card-picker
|
<hui-card-picker
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
import {
|
import {
|
||||||
@ -9,7 +10,6 @@ import {
|
|||||||
internalProperty,
|
internalProperty,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
query,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import {
|
import {
|
||||||
@ -28,34 +28,24 @@ import "../../../../components/ha-card";
|
|||||||
import "../../../../components/ha-formfield";
|
import "../../../../components/ha-formfield";
|
||||||
import "../../../../components/ha-icon";
|
import "../../../../components/ha-icon";
|
||||||
import "../../../../components/ha-switch";
|
import "../../../../components/ha-switch";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import { EntitiesCardConfig } from "../../cards/types";
|
import type { EntitiesCardConfig } from "../../cards/types";
|
||||||
import "../../components/hui-theme-select-editor";
|
import "../../components/hui-theme-select-editor";
|
||||||
import { LovelaceRowConfig } from "../../entity-rows/types";
|
import type { LovelaceRowConfig } from "../../entity-rows/types";
|
||||||
import { headerFooterConfigStructs } from "../../header-footer/types";
|
import { headerFooterConfigStructs } from "../../header-footer/types";
|
||||||
import { LovelaceCardEditor } from "../../types";
|
import type { LovelaceCardEditor } from "../../types";
|
||||||
import "../hui-detail-editor-base";
|
import "../header-footer-editor/hui-header-footer-editor";
|
||||||
import { HuiElementEditor } from "../hui-element-editor";
|
|
||||||
import "../hui-entities-card-row-editor";
|
import "../hui-entities-card-row-editor";
|
||||||
|
import "../hui-sub-element-editor";
|
||||||
import { processEditorEntities } from "../process-editor-entities";
|
import { processEditorEntities } from "../process-editor-entities";
|
||||||
import {
|
import {
|
||||||
EditorTarget,
|
EditorTarget,
|
||||||
|
EditSubElementEvent,
|
||||||
entitiesConfigStruct,
|
entitiesConfigStruct,
|
||||||
EntitiesEditorEvent,
|
SubElementEditorConfig,
|
||||||
GUIModeChangedEvent,
|
|
||||||
} from "../types";
|
} from "../types";
|
||||||
import { configElementStyle } from "./config-elements-style";
|
import { configElementStyle } from "./config-elements-style";
|
||||||
|
|
||||||
interface EditRowEvent {
|
|
||||||
index: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HASSDomEvents {
|
|
||||||
"edit-row": EditRowEvent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const cardConfigStruct = object({
|
const cardConfigStruct = object({
|
||||||
type: string(),
|
type: string(),
|
||||||
title: optional(union([string(), boolean()])),
|
title: optional(union([string(), boolean()])),
|
||||||
@ -76,15 +66,7 @@ export class HuiEntitiesCardEditor extends LitElement
|
|||||||
|
|
||||||
@internalProperty() private _configEntities?: LovelaceRowConfig[];
|
@internalProperty() private _configEntities?: LovelaceRowConfig[];
|
||||||
|
|
||||||
@internalProperty() private _editRowConfig?: LovelaceRowConfig;
|
@internalProperty() private _subElementEditorConfig?: SubElementEditorConfig;
|
||||||
|
|
||||||
@internalProperty() private _editRowIndex?: number;
|
|
||||||
|
|
||||||
@internalProperty() private _editRowGuiModeAvailable? = true;
|
|
||||||
|
|
||||||
@internalProperty() private _editRowGuiMode? = true;
|
|
||||||
|
|
||||||
@query("hui-element-editor") private _cardEditorEl?: HuiElementEditor;
|
|
||||||
|
|
||||||
public setConfig(config: EntitiesCardConfig): void {
|
public setConfig(config: EntitiesCardConfig): void {
|
||||||
assert(config, cardConfigStruct);
|
assert(config, cardConfigStruct);
|
||||||
@ -105,28 +87,15 @@ export class HuiEntitiesCardEditor extends LitElement
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._editRowConfig) {
|
if (this._subElementEditorConfig) {
|
||||||
return html`
|
return html`
|
||||||
<hui-detail-editor-base
|
<hui-sub-element-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.guiModeAvailable=${this._editRowGuiModeAvailable}
|
.config=${this._subElementEditorConfig}
|
||||||
.guiMode=${this._editRowGuiMode}
|
|
||||||
@toggle-gui-mode=${this._toggleMode}
|
|
||||||
@go-back=${this._goBack}
|
@go-back=${this._goBack}
|
||||||
|
@config-changed=${this._handleSubElementChanged}
|
||||||
>
|
>
|
||||||
<span slot="title"
|
</hui-sub-element-editor>
|
||||||
>${this.hass.localize(
|
|
||||||
"ui.panel.lovelace.editor.card.entities.entity_row_editor"
|
|
||||||
)}</span
|
|
||||||
>
|
|
||||||
<hui-element-editor
|
|
||||||
.hass=${this.hass}
|
|
||||||
.value=${this._editRowConfig}
|
|
||||||
elementType="row"
|
|
||||||
@config-changed=${this._handleEntityRowConfigChanged}
|
|
||||||
@GUImode-changed=${this._handleGUIModeChanged}
|
|
||||||
></hui-element-editor>
|
|
||||||
</hui-detail-editor-base>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,43 +143,75 @@ export class HuiEntitiesCardEditor extends LitElement
|
|||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
</div>
|
</div>
|
||||||
|
<hui-header-footer-editor
|
||||||
|
.hass=${this.hass}
|
||||||
|
.configValue=${"header"}
|
||||||
|
.config=${this._config.header}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
@edit-detail-element=${this._editDetailElement}
|
||||||
|
></hui-header-footer-editor>
|
||||||
|
<hui-header-footer-editor
|
||||||
|
.hass=${this.hass}
|
||||||
|
.configValue=${"footer"}
|
||||||
|
.config=${this._config.footer}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
@edit-detail-element=${this._editDetailElement}
|
||||||
|
></hui-header-footer-editor>
|
||||||
</div>
|
</div>
|
||||||
<hui-entities-card-row-editor
|
<hui-entities-card-row-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.entities=${this._configEntities}
|
.entities=${this._configEntities}
|
||||||
@entities-changed=${this._valueChanged}
|
@entities-changed=${this._valueChanged}
|
||||||
@edit-row=${this._editRow}
|
@edit-detail-element=${this._editDetailElement}
|
||||||
></hui-entities-card-row-editor>
|
></hui-entities-card-row-editor>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
ev.stopPropagation();
|
||||||
if (!this._config || !this.hass) {
|
if (!this._config || !this.hass) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = ev.target! as EditorTarget;
|
const target = ev.target! as EditorTarget;
|
||||||
|
const configValue =
|
||||||
|
target.configValue || this._subElementEditorConfig?.type;
|
||||||
|
const value =
|
||||||
|
target.checked !== undefined
|
||||||
|
? target.checked
|
||||||
|
: target.value || ev.detail.config || ev.detail.value;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(target.configValue! === "title" && target.value === this._title) ||
|
(configValue! === "title" && target.value === this._title) ||
|
||||||
(target.configValue! === "theme" && target.value === this._theme)
|
(configValue! === "theme" && target.value === this._theme)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.detail && ev.detail.entities) {
|
if (configValue === "row" || (ev.detail && ev.detail.entities)) {
|
||||||
this._config = { ...this._config, entities: ev.detail.entities };
|
const newConfigEntities =
|
||||||
|
ev.detail.entities || this._configEntities!.concat();
|
||||||
|
if (configValue === "row") {
|
||||||
|
if (!value) {
|
||||||
|
newConfigEntities.splice(this._subElementEditorConfig!.index!, 1);
|
||||||
|
this._goBack();
|
||||||
|
} else {
|
||||||
|
newConfigEntities[this._subElementEditorConfig!.index!] = value;
|
||||||
|
}
|
||||||
|
|
||||||
this._configEntities = processEditorEntities(this._config.entities);
|
this._subElementEditorConfig!.elementConfig = value;
|
||||||
} else if (target.configValue) {
|
}
|
||||||
if (target.value === "") {
|
|
||||||
|
this._config = { ...this._config!, entities: newConfigEntities };
|
||||||
|
this._configEntities = processEditorEntities(this._config!.entities);
|
||||||
|
} else if (configValue) {
|
||||||
|
if (value === "") {
|
||||||
this._config = { ...this._config };
|
this._config = { ...this._config };
|
||||||
delete this._config[target.configValue!];
|
delete this._config[configValue!];
|
||||||
} else {
|
} else {
|
||||||
this._config = {
|
this._config = {
|
||||||
...this._config,
|
...this._config,
|
||||||
[target.configValue]:
|
[configValue]: value,
|
||||||
target.checked !== undefined ? target.checked : target.value,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,49 +219,52 @@ export class HuiEntitiesCardEditor extends LitElement
|
|||||||
fireEvent(this, "config-changed", { config: this._config });
|
fireEvent(this, "config-changed", { config: this._config });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _editRow(ev: HASSDomEvent<EditRowEvent>): void {
|
private _handleSubElementChanged(ev: CustomEvent): void {
|
||||||
this._editRowIndex = ev.detail.index;
|
ev.stopPropagation();
|
||||||
this._editRowConfig = this._configEntities![this._editRowIndex];
|
if (!this._config || !this.hass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const configValue = this._subElementEditorConfig?.type;
|
||||||
|
const value = ev.detail.config;
|
||||||
|
|
||||||
|
if (configValue === "row") {
|
||||||
|
const newConfigEntities = this._configEntities!.concat();
|
||||||
|
if (!value) {
|
||||||
|
newConfigEntities.splice(this._subElementEditorConfig!.index!, 1);
|
||||||
|
this._goBack();
|
||||||
|
} else {
|
||||||
|
newConfigEntities[this._subElementEditorConfig!.index!] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._config = { ...this._config!, entities: newConfigEntities };
|
||||||
|
this._configEntities = processEditorEntities(this._config!.entities);
|
||||||
|
} else if (configValue) {
|
||||||
|
if (value === "") {
|
||||||
|
this._config = { ...this._config };
|
||||||
|
delete this._config[configValue!];
|
||||||
|
} else {
|
||||||
|
this._config = {
|
||||||
|
...this._config,
|
||||||
|
[configValue]: value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._subElementEditorConfig = {
|
||||||
|
...this._subElementEditorConfig!,
|
||||||
|
elementConfig: value,
|
||||||
|
};
|
||||||
|
|
||||||
|
fireEvent(this, "config-changed", { config: this._config });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _editDetailElement(ev: HASSDomEvent<EditSubElementEvent>): void {
|
||||||
|
this._subElementEditorConfig = ev.detail.subElementConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _goBack(): void {
|
private _goBack(): void {
|
||||||
this._editRowIndex = undefined;
|
this._subElementEditorConfig = undefined;
|
||||||
this._editRowConfig = undefined;
|
|
||||||
this._editRowGuiModeAvailable = true;
|
|
||||||
this._editRowGuiMode = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _toggleMode(): void {
|
|
||||||
this._cardEditorEl?.toggleMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleEntityRowConfigChanged(ev: CustomEvent): void {
|
|
||||||
ev.stopPropagation();
|
|
||||||
const value = ev.detail.config as LovelaceRowConfig;
|
|
||||||
this._editRowGuiModeAvailable = ev.detail.guiModeAvailable;
|
|
||||||
|
|
||||||
const newConfigEntities = this._configEntities!.concat();
|
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
newConfigEntities.splice(this._editRowIndex!, 1);
|
|
||||||
this._goBack();
|
|
||||||
} else {
|
|
||||||
newConfigEntities[this._editRowIndex!] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._editRowConfig = value;
|
|
||||||
|
|
||||||
this._config = { ...this._config!, entities: newConfigEntities };
|
|
||||||
|
|
||||||
this._configEntities = processEditorEntities(this._config!.entities);
|
|
||||||
|
|
||||||
fireEvent(this, "config-changed", { config: this._config! });
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleGUIModeChanged(ev: HASSDomEvent<GUIModeChangedEvent>): void {
|
|
||||||
ev.stopPropagation();
|
|
||||||
this._editRowGuiMode = ev.detail.guiMode;
|
|
||||||
this._editRowGuiModeAvailable = ev.detail.guiModeAvailable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultArray {
|
static get styles(): CSSResultArray {
|
||||||
@ -273,6 +277,10 @@ export class HuiEntitiesCardEditor extends LitElement
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hui-header-footer-editor {
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,156 @@
|
|||||||
|
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import {
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
internalProperty,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import { assert } from "superstruct";
|
||||||
|
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import "../../../../components/entity/ha-entity-picker";
|
||||||
|
import "../../../../components/ha-formfield";
|
||||||
|
import "../../../../components/ha-switch";
|
||||||
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import {
|
||||||
|
GraphHeaderFooterConfig,
|
||||||
|
graphHeaderFooterConfigStruct,
|
||||||
|
} from "../../header-footer/types";
|
||||||
|
import type { LovelaceCardEditor } from "../../types";
|
||||||
|
import type { EditorTarget, EntitiesEditorEvent } from "../types";
|
||||||
|
import { configElementStyle } from "./config-elements-style";
|
||||||
|
|
||||||
|
const includeDomains = ["sensor"];
|
||||||
|
|
||||||
|
@customElement("hui-graph-footer-editor")
|
||||||
|
export class HuiGraphFooterEditor extends LitElement
|
||||||
|
implements LovelaceCardEditor {
|
||||||
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
|
@internalProperty() private _config?: GraphHeaderFooterConfig;
|
||||||
|
|
||||||
|
public setConfig(config: GraphHeaderFooterConfig): void {
|
||||||
|
assert(config, graphHeaderFooterConfigStruct);
|
||||||
|
this._config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
get _entity(): string {
|
||||||
|
return this._config!.entity || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
get _detail(): number {
|
||||||
|
return this._config!.detail ?? 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
get _hours_to_show(): number {
|
||||||
|
return this._config!.hours_to_show || 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass || !this._config) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="card-config">
|
||||||
|
<ha-entity-picker
|
||||||
|
allow-custom-entity
|
||||||
|
.label="${this.hass.localize(
|
||||||
|
"ui.panel.lovelace.editor.card.generic.entity"
|
||||||
|
)} (${this.hass.localize(
|
||||||
|
"ui.panel.lovelace.editor.card.config.required"
|
||||||
|
)})"
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this._entity}
|
||||||
|
.configValue=${"entity"}
|
||||||
|
.includeDomains=${includeDomains}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-entity-picker>
|
||||||
|
<div class="side-by-side">
|
||||||
|
<ha-formfield
|
||||||
|
label=${this.hass.localize(
|
||||||
|
"ui.panel.lovelace.editor.card.sensor.show_more_detail"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${this._detail === 2}
|
||||||
|
.configValue=${"detail"}
|
||||||
|
@change=${this._change}
|
||||||
|
></ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
|
<paper-input
|
||||||
|
type="number"
|
||||||
|
.label="${this.hass.localize(
|
||||||
|
"ui.panel.lovelace.editor.card.generic.hours_to_show"
|
||||||
|
)} (${this.hass.localize(
|
||||||
|
"ui.panel.lovelace.editor.card.config.optional"
|
||||||
|
)})"
|
||||||
|
.value=${this._hours_to_show}
|
||||||
|
.configValue=${"hours_to_show"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _change(ev: Event) {
|
||||||
|
if (!this._config || !this.hass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = (ev.target! as EditorTarget).checked ? 2 : 1;
|
||||||
|
|
||||||
|
if (this._detail === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._config = {
|
||||||
|
...this._config,
|
||||||
|
detail: value,
|
||||||
|
};
|
||||||
|
|
||||||
|
fireEvent(this, "config-changed", { config: this._config });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: HASSDomEvent<EntitiesEditorEvent>): void {
|
||||||
|
if (!this._config || !this.hass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target = ev.target! as EditorTarget;
|
||||||
|
|
||||||
|
if (this[`_${target.configValue}`] === target.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (target.configValue) {
|
||||||
|
if (
|
||||||
|
target.value === "" ||
|
||||||
|
(target.type === "number" && isNaN(Number(target.value)))
|
||||||
|
) {
|
||||||
|
this._config = { ...this._config };
|
||||||
|
delete this._config[target.configValue!];
|
||||||
|
} else {
|
||||||
|
let value: any = target.value;
|
||||||
|
if (target.type === "number") {
|
||||||
|
value = Number(value);
|
||||||
|
}
|
||||||
|
this._config = { ...this._config, [target.configValue!]: value };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fireEvent(this, "config-changed", { config: this._config });
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return configElementStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-graph-footer-editor": HuiGraphFooterEditor;
|
||||||
|
}
|
||||||
|
}
|
@ -18,12 +18,10 @@ import { LovelaceCardConfig, LovelaceConfig } from "../../../../data/lovelace";
|
|||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { StackCardConfig } from "../../cards/types";
|
import { StackCardConfig } from "../../cards/types";
|
||||||
import { LovelaceCardEditor } from "../../types";
|
import { LovelaceCardEditor } from "../../types";
|
||||||
|
import "../card-editor/hui-card-element-editor";
|
||||||
|
import type { HuiCardElementEditor } from "../card-editor/hui-card-element-editor";
|
||||||
import "../card-editor/hui-card-picker";
|
import "../card-editor/hui-card-picker";
|
||||||
import "../hui-element-editor";
|
import type { ConfigChangedEvent } from "../hui-element-editor";
|
||||||
import type {
|
|
||||||
ConfigChangedEvent,
|
|
||||||
HuiElementEditor,
|
|
||||||
} from "../hui-element-editor";
|
|
||||||
import { GUIModeChangedEvent } from "../types";
|
import { GUIModeChangedEvent } from "../types";
|
||||||
|
|
||||||
const cardConfigStruct = object({
|
const cardConfigStruct = object({
|
||||||
@ -47,7 +45,8 @@ export class HuiStackCardEditor extends LitElement
|
|||||||
|
|
||||||
@internalProperty() private _guiModeAvailable? = true;
|
@internalProperty() private _guiModeAvailable? = true;
|
||||||
|
|
||||||
@query("hui-element-editor") private _cardEditorEl?: HuiElementEditor;
|
@query("hui-card-element-editor")
|
||||||
|
private _cardEditorEl?: HuiCardElementEditor;
|
||||||
|
|
||||||
public setConfig(config: Readonly<StackCardConfig>): void {
|
public setConfig(config: Readonly<StackCardConfig>): void {
|
||||||
assert(config, cardConfigStruct);
|
assert(config, cardConfigStruct);
|
||||||
@ -140,13 +139,13 @@ export class HuiStackCardEditor extends LitElement
|
|||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hui-element-editor
|
<hui-card-element-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._config.cards[selected]}
|
.value=${this._config.cards[selected]}
|
||||||
.lovelace=${this.lovelace}
|
.lovelace=${this.lovelace}
|
||||||
@config-changed=${this._handleConfigChanged}
|
@config-changed=${this._handleConfigChanged}
|
||||||
@GUImode-changed=${this._handleGUIModeChanged}
|
@GUImode-changed=${this._handleGUIModeChanged}
|
||||||
></hui-element-editor>
|
></hui-card-element-editor>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<hui-card-picker
|
<hui-card-picker
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
import { customElement } from "lit-element";
|
||||||
|
import { getRowElementClass } from "../../create-element/create-row-element";
|
||||||
|
import { LovelaceRowConfig } from "../../entity-rows/types";
|
||||||
|
import type { LovelaceRowEditor } from "../../types";
|
||||||
|
import { HuiElementEditor } from "../hui-element-editor";
|
||||||
|
|
||||||
|
const GENERIC_ROW_TYPE = "generic-row";
|
||||||
|
|
||||||
|
@customElement("hui-row-element-editor")
|
||||||
|
export class HuiRowElementEditor extends HuiElementEditor<LovelaceRowConfig> {
|
||||||
|
protected get configElementType(): string | undefined {
|
||||||
|
if (!this.value?.type && "entity" in this.value!) {
|
||||||
|
return GENERIC_ROW_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.value?.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async getConfigElement(): Promise<LovelaceRowEditor | undefined> {
|
||||||
|
if (this.configElementType! === GENERIC_ROW_TYPE) {
|
||||||
|
return document.createElement("hui-generic-entity-row-editor");
|
||||||
|
}
|
||||||
|
|
||||||
|
const elClass = await getRowElementClass(this.configElementType!);
|
||||||
|
|
||||||
|
// Check if a GUI editor exists
|
||||||
|
if (elClass && elClass.getConfigElement) {
|
||||||
|
return elClass.getConfigElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-row-element-editor": HuiRowElementEditor;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import { getHeaderFooterElementClass } from "../../create-element/create-header-footer-element";
|
||||||
|
import { LovelaceHeaderFooterConfig } from "../../header-footer/types";
|
||||||
|
|
||||||
|
export const getHeaderFooterStubConfig = async (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
type: string,
|
||||||
|
entities: string[],
|
||||||
|
entitiesFallback: string[]
|
||||||
|
): Promise<LovelaceHeaderFooterConfig> => {
|
||||||
|
let config: LovelaceHeaderFooterConfig = { type };
|
||||||
|
|
||||||
|
const elClass = await getHeaderFooterElementClass(type);
|
||||||
|
|
||||||
|
if (elClass && elClass.getStubConfig) {
|
||||||
|
const classStubConfig = elClass.getStubConfig(
|
||||||
|
hass,
|
||||||
|
entities,
|
||||||
|
entitiesFallback
|
||||||
|
);
|
||||||
|
|
||||||
|
config = { ...config, ...classStubConfig };
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
};
|
@ -0,0 +1,176 @@
|
|||||||
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResultArray,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
internalProperty,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { createCloseHeading } from "../../../../components/ha-dialog";
|
||||||
|
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
|
||||||
|
import { haStyleDialog } from "../../../../resources/styles";
|
||||||
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import type { LovelaceHeaderFooterConfig } from "../../header-footer/types";
|
||||||
|
import { headerFooterElements } from "../lovelace-headerfooters";
|
||||||
|
import { getHeaderFooterStubConfig } from "./get-headerfooter-stub-config";
|
||||||
|
import type { CreateHeaderFooterDialogParams } from "./show-create-headerfooter-dialog";
|
||||||
|
|
||||||
|
@customElement("hui-dialog-create-headerfooter")
|
||||||
|
export class HuiCreateDialogHeaderFooter extends LitElement
|
||||||
|
implements HassDialog<CreateHeaderFooterDialogParams> {
|
||||||
|
@property({ attribute: false }) protected hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@internalProperty() private _params?: CreateHeaderFooterDialogParams;
|
||||||
|
|
||||||
|
public async showDialog(
|
||||||
|
params: CreateHeaderFooterDialogParams
|
||||||
|
): Promise<void> {
|
||||||
|
this._params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialog(): boolean {
|
||||||
|
this._params = undefined;
|
||||||
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this._params) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-dialog
|
||||||
|
open
|
||||||
|
scrimClickAction
|
||||||
|
.heading=${createCloseHeading(
|
||||||
|
this.hass,
|
||||||
|
this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.header-footer.choose_header_footer`,
|
||||||
|
"type",
|
||||||
|
this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.header-footer.${this._params.type}`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
@keydown=${this._ignoreKeydown}
|
||||||
|
@closed=${this._cancel}
|
||||||
|
>
|
||||||
|
<div class="elements">
|
||||||
|
${headerFooterElements.map(
|
||||||
|
(headerFooter) =>
|
||||||
|
html`
|
||||||
|
<ha-card
|
||||||
|
outlined
|
||||||
|
.type=${headerFooter.type}
|
||||||
|
@click=${this._handleHeaderFooterPicked}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${headerFooter.icon}></ha-svg-icon>
|
||||||
|
<div>
|
||||||
|
${this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.header-footer.types.${headerFooter.type}.name`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div slot="primaryAction">
|
||||||
|
<mwc-button @click=${this._cancel}>
|
||||||
|
${this.hass!.localize("ui.common.cancel")}
|
||||||
|
</mwc-button>
|
||||||
|
</div>
|
||||||
|
</ha-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _handleHeaderFooterPicked(ev: CustomEvent): Promise<void> {
|
||||||
|
const type = (ev.currentTarget as any).type;
|
||||||
|
let config: LovelaceHeaderFooterConfig = { type };
|
||||||
|
|
||||||
|
if (this.hass) {
|
||||||
|
config = await getHeaderFooterStubConfig(
|
||||||
|
this.hass,
|
||||||
|
type,
|
||||||
|
this._params?.entities || [],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._params!.pickHeaderFooter(config);
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _ignoreKeydown(ev: KeyboardEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _cancel(ev?: Event) {
|
||||||
|
if (ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultArray {
|
||||||
|
return [
|
||||||
|
haStyleDialog,
|
||||||
|
css`
|
||||||
|
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||||
|
/* overrule the ha-style-dialog max-height on small screens */
|
||||||
|
ha-dialog {
|
||||||
|
--mdc-dialog-max-height: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (min-width: 850px) {
|
||||||
|
ha-dialog {
|
||||||
|
--mdc-dialog-min-width: 550px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-dialog {
|
||||||
|
--mdc-dialog-max-width: 550px;
|
||||||
|
--dialog-content-padding: 2px 24px 20px 24px;
|
||||||
|
--dialog-z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.elements {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner,
|
||||||
|
ha-card {
|
||||||
|
width: calc(50% - 8px);
|
||||||
|
text-align: center;
|
||||||
|
margin: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-card {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 8px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-svg-icon {
|
||||||
|
padding-bottom: 4px;
|
||||||
|
--mdc-icon-size: 38px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-dialog-create-headerfooter": HuiCreateDialogHeaderFooter;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
import "@material/mwc-icon-button/mwc-icon-button";
|
||||||
|
import { mdiClose, mdiPencil, mdiPlus } from "@mdi/js";
|
||||||
|
import "@polymer/paper-item/paper-item";
|
||||||
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import "../../../../components/ha-paper-dropdown-menu";
|
||||||
|
import "../../../../components/ha-svg-icon";
|
||||||
|
import type { LovelaceConfig } from "../../../../data/lovelace";
|
||||||
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import type { LovelaceHeaderFooterConfig } from "../../header-footer/types";
|
||||||
|
import { showCreateHeaderFooterDialog } from "./show-create-headerfooter-dialog";
|
||||||
|
|
||||||
|
@customElement("hui-header-footer-editor")
|
||||||
|
export class HuiHeaderFooterEditor extends LitElement {
|
||||||
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
public lovelaceConfig!: LovelaceConfig;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public config?: LovelaceHeaderFooterConfig;
|
||||||
|
|
||||||
|
@property() public configValue!: "header" | "footer";
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
${this.hass.localize(
|
||||||
|
`ui.panel.lovelace.editor.header-footer.${this.configValue}`
|
||||||
|
)}:
|
||||||
|
${!this.config?.type
|
||||||
|
? this.hass!.localize("ui.panel.lovelace.editor.common.none")
|
||||||
|
: this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.header-footer.types.${this.config?.type}.name`
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
${!this.config?.type
|
||||||
|
? html`
|
||||||
|
<mwc-icon-button
|
||||||
|
aria-label=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.common.add"
|
||||||
|
)}
|
||||||
|
class="add-icon"
|
||||||
|
@click=${this._add}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiPlus}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<mwc-icon-button
|
||||||
|
aria-label=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.common.clear"
|
||||||
|
)}
|
||||||
|
class="remove-icon"
|
||||||
|
@click=${this._delete}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
<mwc-icon-button
|
||||||
|
aria-label=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.common.edit"
|
||||||
|
)}
|
||||||
|
class="edit-icon"
|
||||||
|
@click=${this._edit}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiPencil}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _edit(): void {
|
||||||
|
fireEvent(this, "edit-detail-element", {
|
||||||
|
subElementConfig: {
|
||||||
|
elementConfig: this.config,
|
||||||
|
type: this.configValue,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _add(): void {
|
||||||
|
showCreateHeaderFooterDialog(this, {
|
||||||
|
pickHeaderFooter: (config) => this._elementPicked(config),
|
||||||
|
type: this.configValue,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _elementPicked(config: LovelaceHeaderFooterConfig): void {
|
||||||
|
fireEvent(this, "value-changed", { value: config });
|
||||||
|
fireEvent(this, "edit-detail-element", {
|
||||||
|
subElementConfig: {
|
||||||
|
elementConfig: config,
|
||||||
|
type: this.configValue,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _delete(): void {
|
||||||
|
fireEvent(this, "value-changed", { value: "" });
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
:host {
|
||||||
|
font-size: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host > div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
mwc-icon-button,
|
||||||
|
.header-footer-icon {
|
||||||
|
--mdc-icon-button-size: 36px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-footer-icon {
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-header-footer-editor": HuiHeaderFooterEditor;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
import { customElement } from "lit-element";
|
||||||
|
import { getHeaderFooterElementClass } from "../../create-element/create-header-footer-element";
|
||||||
|
import type { LovelaceHeaderFooterConfig } from "../../header-footer/types";
|
||||||
|
import type { LovelaceHeaderFooterEditor } from "../../types";
|
||||||
|
import { HuiElementEditor } from "../hui-element-editor";
|
||||||
|
|
||||||
|
@customElement("hui-headerfooter-element-editor")
|
||||||
|
export class HuiHeaderFooterElementEditor extends HuiElementEditor<
|
||||||
|
LovelaceHeaderFooterConfig
|
||||||
|
> {
|
||||||
|
protected async getConfigElement(): Promise<
|
||||||
|
LovelaceHeaderFooterEditor | undefined
|
||||||
|
> {
|
||||||
|
const elClass = await getHeaderFooterElementClass(this.configElementType!);
|
||||||
|
|
||||||
|
// Check if a GUI editor exists
|
||||||
|
if (elClass && elClass.getConfigElement) {
|
||||||
|
return elClass.getConfigElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-headerfooter-element-editor": HuiHeaderFooterElementEditor;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import type { LovelaceHeaderFooterConfig } from "../../header-footer/types";
|
||||||
|
|
||||||
|
export interface CreateHeaderFooterDialogParams {
|
||||||
|
pickHeaderFooter: (config: LovelaceHeaderFooterConfig) => void;
|
||||||
|
type: "header" | "footer";
|
||||||
|
entities?: string[]; // We can pass entity id's that will be added to the config when a header footer is picked
|
||||||
|
}
|
||||||
|
|
||||||
|
const importCreateHeaderFooterDialog = () =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "hui-dialog-create-headerfooter" */ "./hui-dialog-create-headerfooter"
|
||||||
|
);
|
||||||
|
|
||||||
|
export const showCreateHeaderFooterDialog = (
|
||||||
|
element: HTMLElement,
|
||||||
|
createHeaderFooterDialogParams: CreateHeaderFooterDialogParams
|
||||||
|
): void => {
|
||||||
|
fireEvent(element, "show-dialog", {
|
||||||
|
dialogTag: "hui-dialog-create-headerfooter",
|
||||||
|
dialogImport: importCreateHeaderFooterDialog,
|
||||||
|
dialogParams: createHeaderFooterDialogParams,
|
||||||
|
});
|
||||||
|
};
|
@ -1,86 +0,0 @@
|
|||||||
import "@material/mwc-button";
|
|
||||||
import "@material/mwc-icon-button";
|
|
||||||
import { mdiArrowLeft } from "@mdi/js";
|
|
||||||
import {
|
|
||||||
css,
|
|
||||||
CSSResult,
|
|
||||||
customElement,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
property,
|
|
||||||
TemplateResult,
|
|
||||||
} from "lit-element";
|
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
|
||||||
import "../../../components/ha-svg-icon";
|
|
||||||
import { HomeAssistant } from "../../../types";
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HASSDomEvents {
|
|
||||||
"go-back": undefined;
|
|
||||||
"toggle-gui-mode": undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@customElement("hui-detail-editor-base")
|
|
||||||
export class HuiDetailEditorBase extends LitElement {
|
|
||||||
public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public guiModeAvailable? = true;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public guiMode? = true;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
<div class="header">
|
|
||||||
<div class="back-title">
|
|
||||||
<mwc-icon-button @click=${this._goBack}>
|
|
||||||
<ha-svg-icon .path=${mdiArrowLeft}></ha-svg-icon>
|
|
||||||
</mwc-icon-button>
|
|
||||||
<slot name="title"></slot>
|
|
||||||
</div>
|
|
||||||
<mwc-button
|
|
||||||
slot="secondaryAction"
|
|
||||||
class="gui-mode-button"
|
|
||||||
.disabled=${!this.guiModeAvailable}
|
|
||||||
@click=${this._toggleMode}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
this.guiMode
|
|
||||||
? "ui.panel.lovelace.editor.edit_card.show_code_editor"
|
|
||||||
: "ui.panel.lovelace.editor.edit_card.show_visual_editor"
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
|
||||||
</div>
|
|
||||||
<slot></slot>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _goBack(): void {
|
|
||||||
fireEvent(this, "go-back");
|
|
||||||
}
|
|
||||||
|
|
||||||
private _toggleMode(): void {
|
|
||||||
fireEvent(this, "toggle-gui-mode");
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
|
||||||
return css`
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.back-title {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"hui-detail-editor-base": HuiDetailEditorBase;
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,11 +3,11 @@ import { safeDump, safeLoad } from "js-yaml";
|
|||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
customElement,
|
|
||||||
html,
|
html,
|
||||||
internalProperty,
|
internalProperty,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
|
PropertyValues,
|
||||||
query,
|
query,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
@ -23,60 +23,45 @@ import type {
|
|||||||
} from "../../../data/lovelace";
|
} from "../../../data/lovelace";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { handleStructError } from "../common/structs/handle-errors";
|
import { handleStructError } from "../common/structs/handle-errors";
|
||||||
import { getCardElementClass } from "../create-element/create-card-element";
|
|
||||||
import { getRowElementClass } from "../create-element/create-row-element";
|
|
||||||
import type { LovelaceRowConfig } from "../entity-rows/types";
|
import type { LovelaceRowConfig } from "../entity-rows/types";
|
||||||
import type {
|
import { LovelaceHeaderFooterConfig } from "../header-footer/types";
|
||||||
LovelaceCardConstructor,
|
import type { LovelaceGenericElementEditor } from "../types";
|
||||||
LovelaceCardEditor,
|
|
||||||
LovelaceRowConstructor,
|
|
||||||
LovelaceRowEditor,
|
|
||||||
} from "../types";
|
|
||||||
import "./config-elements/hui-generic-entity-row-editor";
|
import "./config-elements/hui-generic-entity-row-editor";
|
||||||
import { GUISupportError } from "./gui-support-error";
|
import { GUISupportError } from "./gui-support-error";
|
||||||
import { GUIModeChangedEvent } from "./types";
|
import { EditSubElementEvent, GUIModeChangedEvent } from "./types";
|
||||||
|
|
||||||
export interface ConfigChangedEvent {
|
export interface ConfigChangedEvent {
|
||||||
config: LovelaceCardConfig | LovelaceRowConfig;
|
config: LovelaceCardConfig | LovelaceRowConfig | LovelaceHeaderFooterConfig;
|
||||||
error?: string;
|
error?: string;
|
||||||
guiModeAvailable?: boolean;
|
guiModeAvailable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
"entities-changed": {
|
|
||||||
entities: LovelaceRowConfig[];
|
|
||||||
};
|
|
||||||
"config-changed": ConfigChangedEvent;
|
"config-changed": ConfigChangedEvent;
|
||||||
"GUImode-changed": GUIModeChangedEvent;
|
"GUImode-changed": GUIModeChangedEvent;
|
||||||
|
"edit-detail-element": EditSubElementEvent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UIConfigChangedEvent extends Event {
|
export interface UIConfigChangedEvent extends Event {
|
||||||
detail: {
|
detail: {
|
||||||
config: LovelaceCardConfig | LovelaceRowConfig;
|
config: LovelaceCardConfig | LovelaceRowConfig | LovelaceHeaderFooterConfig;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const GENERIC_ROW_TYPE = "generic-row";
|
export abstract class HuiElementEditor<T> extends LitElement {
|
||||||
|
|
||||||
@customElement("hui-element-editor")
|
|
||||||
export class HuiElementEditor extends LitElement {
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public lovelace?: LovelaceConfig;
|
@property({ attribute: false }) public lovelace?: LovelaceConfig;
|
||||||
|
|
||||||
@property() public elementType: "row" | "card" = "card";
|
|
||||||
|
|
||||||
@internalProperty() private _yaml?: string;
|
@internalProperty() private _yaml?: string;
|
||||||
|
|
||||||
@internalProperty() private _config?: LovelaceCardConfig | LovelaceRowConfig;
|
@internalProperty() private _config?: T;
|
||||||
|
|
||||||
@internalProperty() private _configElement?:
|
@internalProperty() private _configElement?: LovelaceGenericElementEditor;
|
||||||
| LovelaceCardEditor
|
|
||||||
| LovelaceRowEditor;
|
|
||||||
|
|
||||||
@internalProperty() private _configElType?: string;
|
@internalProperty() private _configElementType?: string;
|
||||||
|
|
||||||
@internalProperty() private _GUImode = true;
|
@internalProperty() private _GUImode = true;
|
||||||
|
|
||||||
@ -108,11 +93,11 @@ export class HuiElementEditor extends LitElement {
|
|||||||
this._setConfig();
|
this._setConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get value(): LovelaceCardConfig | LovelaceRowConfig | undefined {
|
public get value(): T | undefined {
|
||||||
return this._config;
|
return this._config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set value(config: LovelaceCardConfig | LovelaceRowConfig | undefined) {
|
public set value(config: T | undefined) {
|
||||||
if (this._config && deepEqual(config, this._config)) {
|
if (this._config && deepEqual(config, this._config)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -122,7 +107,7 @@ export class HuiElementEditor extends LitElement {
|
|||||||
this._setConfig();
|
this._setConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setConfig() {
|
private _setConfig(): void {
|
||||||
if (!this._error) {
|
if (!this._error) {
|
||||||
try {
|
try {
|
||||||
this._updateConfigElement();
|
this._updateConfigElement();
|
||||||
@ -131,8 +116,9 @@ export class HuiElementEditor extends LitElement {
|
|||||||
this._error = err.message;
|
this._error = err.message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fireEvent(this, "config-changed", {
|
fireEvent(this, "config-changed", {
|
||||||
config: this.value!,
|
config: this.value! as any,
|
||||||
error: this._error,
|
error: this._error,
|
||||||
guiModeAvailable: !(this.hasWarning || this.hasError),
|
guiModeAvailable: !(this.hasWarning || this.hasError),
|
||||||
});
|
});
|
||||||
@ -175,6 +161,16 @@ export class HuiElementEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async getConfigElement(): Promise<
|
||||||
|
LovelaceGenericElementEditor | undefined
|
||||||
|
> {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get configElementType(): string | undefined {
|
||||||
|
return this.value ? (this.value as any).type : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
@ -228,8 +224,9 @@ export class HuiElementEditor extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProperties) {
|
protected updated(changedProperties: PropertyValues) {
|
||||||
super.updated(changedProperties);
|
super.updated(changedProperties);
|
||||||
|
|
||||||
if (this._configElement && changedProperties.has("hass")) {
|
if (this._configElement && changedProperties.has("hass")) {
|
||||||
this._configElement.hass = this.hass;
|
this._configElement.hass = this.hass;
|
||||||
}
|
}
|
||||||
@ -245,10 +242,10 @@ export class HuiElementEditor extends LitElement {
|
|||||||
private _handleUIConfigChanged(ev: UIConfigChangedEvent) {
|
private _handleUIConfigChanged(ev: UIConfigChangedEvent) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const config = ev.detail.config;
|
const config = ev.detail.config;
|
||||||
this.value = config;
|
this.value = (config as unknown) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleYAMLChanged(ev) {
|
private _handleYAMLChanged(ev: CustomEvent) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const newYaml = ev.detail.value;
|
const newYaml = ev.detail.value;
|
||||||
if (newYaml !== this.yaml) {
|
if (newYaml !== this.yaml) {
|
||||||
@ -261,69 +258,43 @@ export class HuiElementEditor extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let type: string;
|
let configElement: LovelaceGenericElementEditor | undefined;
|
||||||
|
|
||||||
if (
|
|
||||||
this.elementType === "row" &&
|
|
||||||
!this.value.type &&
|
|
||||||
"entity" in this.value
|
|
||||||
) {
|
|
||||||
type = GENERIC_ROW_TYPE;
|
|
||||||
} else {
|
|
||||||
type = this.value.type!;
|
|
||||||
}
|
|
||||||
|
|
||||||
let configElement = this._configElement;
|
|
||||||
try {
|
try {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._warnings = undefined;
|
this._warnings = undefined;
|
||||||
|
|
||||||
if (this._configElType !== type) {
|
if (this._configElementType !== this.configElementType) {
|
||||||
// If the type has changed, we need to load a new GUI editor
|
// If the type has changed, we need to load a new GUI editor
|
||||||
if (!type) {
|
|
||||||
throw new Error(`No ${this.elementType} type defined`);
|
if (!this.configElementType) {
|
||||||
|
throw new Error(`No type defined`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let elClass:
|
this._configElementType = this.configElementType;
|
||||||
| LovelaceCardConstructor
|
|
||||||
| LovelaceRowConstructor
|
|
||||||
| undefined;
|
|
||||||
|
|
||||||
if (this.elementType === "card") {
|
|
||||||
elClass = await getCardElementClass(type);
|
|
||||||
} else if (this.elementType === "row" && type !== GENERIC_ROW_TYPE) {
|
|
||||||
elClass = await getRowElementClass(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._loading = true;
|
this._loading = true;
|
||||||
// Check if a GUI editor exists
|
configElement = await this.getConfigElement();
|
||||||
if (elClass && elClass.getConfigElement) {
|
|
||||||
configElement = await elClass.getConfigElement();
|
if (!configElement) {
|
||||||
} else if (this.elementType === "row" && type === GENERIC_ROW_TYPE) {
|
throw new Error(
|
||||||
configElement = document.createElement(
|
`No visual editor available for: ${this.configElementType}`
|
||||||
"hui-generic-entity-row-editor"
|
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
configElement = undefined;
|
|
||||||
throw new GUISupportError(`No visual editor available for: ${type}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._configElement = configElement;
|
configElement.hass = this.hass;
|
||||||
this._configElType = type;
|
if ("lovelace" in configElement) {
|
||||||
|
configElement.lovelace = this.lovelace;
|
||||||
// Perform final setup
|
|
||||||
this._configElement.hass = this.hass;
|
|
||||||
if ("lovelace" in this._configElement) {
|
|
||||||
this._configElement.lovelace = this.lovelace;
|
|
||||||
}
|
}
|
||||||
this._configElement.addEventListener("config-changed", (ev) =>
|
configElement.addEventListener("config-changed", (ev) =>
|
||||||
this._handleUIConfigChanged(ev as UIConfigChangedEvent)
|
this._handleUIConfigChanged(ev as UIConfigChangedEvent)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this._configElement = configElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup GUI editor and check that it can handle the current config
|
// Setup GUI editor and check that it can handle the current config
|
||||||
try {
|
try {
|
||||||
// @ts-ignore
|
|
||||||
this._configElement!.setConfig(this.value);
|
this._configElement!.setConfig(this.value);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new GUISupportError(
|
throw new GUISupportError(
|
||||||
@ -379,9 +350,3 @@ export class HuiElementEditor extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"hui-element-editor": HuiElementEditor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -25,6 +25,14 @@ import { sortableStyles } from "../../../resources/ha-sortable-style";
|
|||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { EntityConfig, LovelaceRowConfig } from "../entity-rows/types";
|
import { EntityConfig, LovelaceRowConfig } from "../entity-rows/types";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"entities-changed": {
|
||||||
|
entities: LovelaceRowConfig[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@customElement("hui-entities-card-row-editor")
|
@customElement("hui-entities-card-row-editor")
|
||||||
export class HuiEntitiesCardRowEditor extends LitElement {
|
export class HuiEntitiesCardRowEditor extends LitElement {
|
||||||
@property({ attribute: false }) protected hass?: HomeAssistant;
|
@property({ attribute: false }) protected hass?: HomeAssistant;
|
||||||
@ -232,8 +240,13 @@ export class HuiEntitiesCardRowEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _editRow(ev: CustomEvent): void {
|
private _editRow(ev: CustomEvent): void {
|
||||||
fireEvent(this, "edit-row", {
|
const index = (ev.currentTarget as any).index;
|
||||||
index: (ev.currentTarget as any).index,
|
fireEvent(this, "edit-detail-element", {
|
||||||
|
subElementConfig: {
|
||||||
|
index,
|
||||||
|
type: "row",
|
||||||
|
elementConfig: this.entities![index],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
132
src/panels/lovelace/editor/hui-sub-element-editor.ts
Normal file
132
src/panels/lovelace/editor/hui-sub-element-editor.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
import "@material/mwc-icon-button";
|
||||||
|
import { mdiArrowLeft } from "@mdi/js";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
internalProperty,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event";
|
||||||
|
import "../../../components/ha-svg-icon";
|
||||||
|
import type { HomeAssistant } from "../../../types";
|
||||||
|
import type { LovelaceRowConfig } from "../entity-rows/types";
|
||||||
|
import type { LovelaceHeaderFooterConfig } from "../header-footer/types";
|
||||||
|
import "./entity-row-editor/hui-row-element-editor";
|
||||||
|
import "./header-footer-editor/hui-header-footer-element-editor";
|
||||||
|
import type { HuiElementEditor } from "./hui-element-editor";
|
||||||
|
import type { GUIModeChangedEvent, SubElementEditorConfig } from "./types";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"go-back": undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement("hui-sub-element-editor")
|
||||||
|
export class HuiSubElementEditor extends LitElement {
|
||||||
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public config!: SubElementEditorConfig;
|
||||||
|
|
||||||
|
@internalProperty() private _guiModeAvailable = true;
|
||||||
|
|
||||||
|
@internalProperty() private _guiMode = true;
|
||||||
|
|
||||||
|
@query(".editor") private _editorElement?: HuiElementEditor<
|
||||||
|
LovelaceRowConfig | LovelaceHeaderFooterConfig
|
||||||
|
>;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="header">
|
||||||
|
<div class="back-title">
|
||||||
|
<mwc-icon-button @click=${this._goBack}>
|
||||||
|
<ha-svg-icon .path=${mdiArrowLeft}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
<span slot="title"
|
||||||
|
>${this.hass.localize(
|
||||||
|
`ui.panel.lovelace.editor.sub-element-editor.types.${this.config?.type}`
|
||||||
|
)}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<mwc-button
|
||||||
|
slot="secondaryAction"
|
||||||
|
.disabled=${!this._guiModeAvailable}
|
||||||
|
@click=${this._toggleMode}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
this._guiMode
|
||||||
|
? "ui.panel.lovelace.editor.edit_card.show_code_editor"
|
||||||
|
: "ui.panel.lovelace.editor.edit_card.show_visual_editor"
|
||||||
|
)}
|
||||||
|
</mwc-button>
|
||||||
|
</div>
|
||||||
|
${this.config.type === "row"
|
||||||
|
? html`
|
||||||
|
<hui-row-element-editor
|
||||||
|
class="editor"
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this.config.elementConfig}
|
||||||
|
@config-changed=${this._handleConfigChanged}
|
||||||
|
@GUImode-changed=${this._handleGUIModeChanged}
|
||||||
|
></hui-row-element-editor>
|
||||||
|
`
|
||||||
|
: this.config.type === "header" || this.config.type === "footer"
|
||||||
|
? html`
|
||||||
|
<hui-headerfooter-element-editor
|
||||||
|
class="editor"
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this.config.elementConfig}
|
||||||
|
@config-changed=${this._handleConfigChanged}
|
||||||
|
@GUImode-changed=${this._handleGUIModeChanged}
|
||||||
|
></hui-headerfooter-element-editor>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _goBack(): void {
|
||||||
|
fireEvent(this, "go-back");
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleMode(): void {
|
||||||
|
this._editorElement?.toggleMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleGUIModeChanged(ev: HASSDomEvent<GUIModeChangedEvent>): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._guiMode = ev.detail.guiMode;
|
||||||
|
this._guiModeAvailable = ev.detail.guiModeAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleConfigChanged(ev: CustomEvent): void {
|
||||||
|
this._guiModeAvailable = ev.detail.guiModeAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.back-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-sub-element-editor": HuiSubElementEditor;
|
||||||
|
}
|
||||||
|
}
|
12
src/panels/lovelace/editor/lovelace-headerfooters.ts
Normal file
12
src/panels/lovelace/editor/lovelace-headerfooters.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import {
|
||||||
|
mdiChartBellCurveCumulative,
|
||||||
|
mdiGestureTapButton,
|
||||||
|
mdiImageArea,
|
||||||
|
} from "@mdi/js";
|
||||||
|
import { HeaderFooter } from "./types";
|
||||||
|
|
||||||
|
export const headerFooterElements: HeaderFooter[] = [
|
||||||
|
{ type: "graph", icon: mdiChartBellCurveCumulative },
|
||||||
|
{ type: "buttons", icon: mdiGestureTapButton },
|
||||||
|
{ type: "picture", icon: mdiImageArea },
|
||||||
|
];
|
@ -14,7 +14,8 @@ import {
|
|||||||
LovelaceViewConfig,
|
LovelaceViewConfig,
|
||||||
ShowViewConfig,
|
ShowViewConfig,
|
||||||
} from "../../../data/lovelace";
|
} from "../../../data/lovelace";
|
||||||
import { EntityConfig } from "../entity-rows/types";
|
import { EntityConfig, LovelaceRowConfig } from "../entity-rows/types";
|
||||||
|
import { LovelaceHeaderFooterConfig } from "../header-footer/types";
|
||||||
|
|
||||||
export interface YamlChangedEvent extends Event {
|
export interface YamlChangedEvent extends Event {
|
||||||
detail: {
|
detail: {
|
||||||
@ -72,10 +73,25 @@ export interface Card {
|
|||||||
isCustom?: boolean;
|
isCustom?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface HeaderFooter {
|
||||||
|
type: string;
|
||||||
|
icon?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CardPickTarget extends EventTarget {
|
export interface CardPickTarget extends EventTarget {
|
||||||
config: LovelaceCardConfig;
|
config: LovelaceCardConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SubElementEditorConfig {
|
||||||
|
index?: number;
|
||||||
|
elementConfig?: LovelaceRowConfig | LovelaceHeaderFooterConfig;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EditSubElementEvent {
|
||||||
|
subElementConfig: SubElementEditorConfig;
|
||||||
|
}
|
||||||
|
|
||||||
export const actionConfigStruct = object({
|
export const actionConfigStruct = object({
|
||||||
action: string(),
|
action: string(),
|
||||||
navigation_path: optional(string()),
|
navigation_path: optional(string()),
|
||||||
|
@ -13,10 +13,11 @@ import {
|
|||||||
import "../../../components/ha-circular-progress";
|
import "../../../components/ha-circular-progress";
|
||||||
import { fetchRecent } from "../../../data/history";
|
import { fetchRecent } from "../../../data/history";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { findEntities } from "../common/find-entites";
|
||||||
import { coordinates } from "../common/graph/coordinates";
|
import { coordinates } from "../common/graph/coordinates";
|
||||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
import "../components/hui-graph-base";
|
import "../components/hui-graph-base";
|
||||||
import { LovelaceHeaderFooter } from "../types";
|
import { LovelaceHeaderFooter, LovelaceHeaderFooterEditor } from "../types";
|
||||||
import { GraphHeaderFooterConfig } from "./types";
|
import { GraphHeaderFooterConfig } from "./types";
|
||||||
|
|
||||||
const MINUTE = 60000;
|
const MINUTE = 60000;
|
||||||
@ -25,8 +26,40 @@ const DAY = 86400000;
|
|||||||
@customElement("hui-graph-header-footer")
|
@customElement("hui-graph-header-footer")
|
||||||
export class HuiGraphHeaderFooter extends LitElement
|
export class HuiGraphHeaderFooter extends LitElement
|
||||||
implements LovelaceHeaderFooter {
|
implements LovelaceHeaderFooter {
|
||||||
public static getStubConfig(): Record<string, unknown> {
|
public static async getConfigElement(): Promise<LovelaceHeaderFooterEditor> {
|
||||||
return {};
|
await import(
|
||||||
|
/* webpackChunkName: "hui-graph-footer-editor" */ "../editor/config-elements/hui-graph-footer-editor"
|
||||||
|
);
|
||||||
|
return document.createElement("hui-graph-footer-editor");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getStubConfig(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entities: string[],
|
||||||
|
entitiesFallback: string[]
|
||||||
|
): GraphHeaderFooterConfig {
|
||||||
|
const includeDomains = ["sensor"];
|
||||||
|
const maxEntities = 1;
|
||||||
|
const entityFilter = (stateObj: HassEntity): boolean => {
|
||||||
|
return (
|
||||||
|
!isNaN(Number(stateObj.state)) &&
|
||||||
|
!!stateObj.attributes.unit_of_measurement
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const foundEntities = findEntities(
|
||||||
|
hass,
|
||||||
|
maxEntities,
|
||||||
|
entities,
|
||||||
|
entitiesFallback,
|
||||||
|
includeDomains,
|
||||||
|
entityFilter
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "graph",
|
||||||
|
entity: foundEntities[0] || "",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
@ -49,6 +49,16 @@ export interface LovelaceCardConstructor extends Constructor<LovelaceCard> {
|
|||||||
getConfigElement?: () => LovelaceCardEditor;
|
getConfigElement?: () => LovelaceCardEditor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LovelaceHeaderFooterConstructor
|
||||||
|
extends Constructor<LovelaceHeaderFooter> {
|
||||||
|
getStubConfig?: (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entities: string[],
|
||||||
|
entitiesFallback: string[]
|
||||||
|
) => LovelaceHeaderFooterConfig;
|
||||||
|
getConfigElement?: () => LovelaceHeaderFooterEditor;
|
||||||
|
}
|
||||||
|
|
||||||
export interface LovelaceRowConstructor extends Constructor<LovelaceRow> {
|
export interface LovelaceRowConstructor extends Constructor<LovelaceRow> {
|
||||||
getConfigElement?: () => LovelaceRowEditor;
|
getConfigElement?: () => LovelaceRowEditor;
|
||||||
}
|
}
|
||||||
@ -59,15 +69,22 @@ export interface LovelaceHeaderFooter extends HTMLElement {
|
|||||||
setConfig(config: LovelaceHeaderFooterConfig): void;
|
setConfig(config: LovelaceHeaderFooterConfig): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LovelaceCardEditor extends HTMLElement {
|
export interface LovelaceCardEditor extends LovelaceGenericElementEditor {
|
||||||
hass?: HomeAssistant;
|
|
||||||
lovelace?: LovelaceConfig;
|
|
||||||
setConfig(config: LovelaceCardConfig): void;
|
setConfig(config: LovelaceCardConfig): void;
|
||||||
refreshYamlEditor?: (focus: boolean) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LovelaceRowEditor extends HTMLElement {
|
export interface LovelaceHeaderFooterEditor
|
||||||
hass?: HomeAssistant;
|
extends LovelaceGenericElementEditor {
|
||||||
|
setConfig(config: LovelaceHeaderFooterConfig): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LovelaceRowEditor extends LovelaceGenericElementEditor {
|
||||||
setConfig(config: LovelaceRowConfig): void;
|
setConfig(config: LovelaceRowConfig): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LovelaceGenericElementEditor extends HTMLElement {
|
||||||
|
hass?: HomeAssistant;
|
||||||
|
lovelace?: LovelaceConfig;
|
||||||
|
setConfig(config: any): void;
|
||||||
refreshYamlEditor?: (focus: boolean) => void;
|
refreshYamlEditor?: (focus: boolean) => void;
|
||||||
}
|
}
|
||||||
|
@ -2369,6 +2369,12 @@
|
|||||||
"open": "Open Lovelace UI menu",
|
"open": "Open Lovelace UI menu",
|
||||||
"raw_editor": "Raw configuration editor"
|
"raw_editor": "Raw configuration editor"
|
||||||
},
|
},
|
||||||
|
"common": {
|
||||||
|
"edit": "Edit",
|
||||||
|
"clear": "Clear",
|
||||||
|
"add": "Add",
|
||||||
|
"none": "None"
|
||||||
|
},
|
||||||
"raw_editor": {
|
"raw_editor": {
|
||||||
"header": "Edit Configuration",
|
"header": "Edit Configuration",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
@ -2420,6 +2426,7 @@
|
|||||||
"show_code_editor": "Show Code Editor",
|
"show_code_editor": "Show Code Editor",
|
||||||
"add": "Add Card",
|
"add": "Add Card",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
|
"clear": "Clear",
|
||||||
"delete": "Delete card",
|
"delete": "Delete card",
|
||||||
"duplicate": "Duplicate card",
|
"duplicate": "Duplicate card",
|
||||||
"move": "Move to View",
|
"move": "Move to View",
|
||||||
@ -2696,6 +2703,29 @@
|
|||||||
"entity": "Entity",
|
"entity": "Entity",
|
||||||
"by_entity": "By Entity",
|
"by_entity": "By Entity",
|
||||||
"by_card": "By Card"
|
"by_card": "By Card"
|
||||||
|
},
|
||||||
|
"header-footer": {
|
||||||
|
"header": "Header",
|
||||||
|
"footer": "Footer",
|
||||||
|
"choose_header_footer": "Choose a {type}",
|
||||||
|
"types": {
|
||||||
|
"graph": {
|
||||||
|
"name": "Graph"
|
||||||
|
},
|
||||||
|
"picture": {
|
||||||
|
"name": "Picture"
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"name": "Buttons"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sub-element-editor": {
|
||||||
|
"types": {
|
||||||
|
"header": "Header Editor",
|
||||||
|
"footer": "Footer Editor",
|
||||||
|
"row": "Entity Row Editor"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user