Merge 6eb0bf154b
into cff54b73a4
This commit is contained in:
commit
3d0d0a7063
|
@ -12,6 +12,8 @@ export class HaBooleanSelector extends LitElement {
|
|||
|
||||
@property({ type: Boolean }) public value = false;
|
||||
|
||||
@property() public placeholder?: any;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property() public helper?: string;
|
||||
|
@ -22,7 +24,7 @@ export class HaBooleanSelector extends LitElement {
|
|||
return html`
|
||||
<ha-formfield alignEnd spaceBetween .label=${this.label}>
|
||||
<ha-switch
|
||||
.checked=${this.value}
|
||||
.checked=${this.value ?? this.placeholder === true}
|
||||
@change=${this._handleChange}
|
||||
.disabled=${this.disabled}
|
||||
></ha-switch>
|
||||
|
|
|
@ -13,12 +13,17 @@ import { ImageEntity, computeImageUrl } from "../../../data/image";
|
|||
import { HomeAssistant } from "../../../types";
|
||||
import { findEntities } from "../common/find-entities";
|
||||
import { LovelaceElement, LovelaceElementConfig } from "../elements/types";
|
||||
import { LovelaceCard } from "../types";
|
||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import { createStyledHuiElement } from "./picture-elements/create-styled-hui-element";
|
||||
import { PictureElementsCardConfig } from "./types";
|
||||
|
||||
@customElement("hui-picture-elements-card")
|
||||
class HuiPictureElementsCard extends LitElement implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
await import("../editor/config-elements/hui-picture-elements-card-editor");
|
||||
return document.createElement("hui-picture-elements-card-editor");
|
||||
}
|
||||
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _elements?: LovelaceElement[];
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
LovelaceCardConstructor,
|
||||
LovelaceCardFeature,
|
||||
LovelaceCardFeatureConstructor,
|
||||
LovelaceElementConstructor,
|
||||
LovelaceHeaderFooter,
|
||||
LovelaceHeaderFooterConstructor,
|
||||
LovelaceRowConstructor,
|
||||
|
@ -44,7 +45,7 @@ interface CreateElementConfigTypes {
|
|||
element: {
|
||||
config: LovelaceElementConfig;
|
||||
element: LovelaceElement;
|
||||
constructor: unknown;
|
||||
constructor: LovelaceElementConstructor;
|
||||
};
|
||||
row: {
|
||||
config: LovelaceRowConfig;
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import "../elements/hui-conditional-element";
|
||||
import "../elements/hui-icon-element";
|
||||
import "../elements/hui-image-element";
|
||||
import "../elements/hui-service-button-element";
|
||||
import "../elements/hui-state-badge-element";
|
||||
import "../elements/hui-state-icon-element";
|
||||
import "../elements/hui-state-label-element";
|
||||
import { LovelaceElementConfig } from "../elements/types";
|
||||
import {
|
||||
createLovelaceElement,
|
||||
getLovelaceElementClass,
|
||||
} from "./create-element-base";
|
||||
|
||||
const ALWAYS_LOADED_TYPES = new Set([
|
||||
"conditional",
|
||||
"icon",
|
||||
"image",
|
||||
"service-button",
|
||||
"state-badge",
|
||||
"state-icon",
|
||||
"state-label",
|
||||
]);
|
||||
|
||||
const LAZY_LOAD_TYPES = {};
|
||||
|
||||
export const createPictureElementElement = (config: LovelaceElementConfig) =>
|
||||
createLovelaceElement(
|
||||
"element",
|
||||
config,
|
||||
ALWAYS_LOADED_TYPES,
|
||||
LAZY_LOAD_TYPES,
|
||||
undefined, // DOMAIN_TO_ELEMENT_TYPE,
|
||||
undefined
|
||||
);
|
||||
|
||||
export const getPictureElementClass = (type: string) =>
|
||||
getLovelaceElementClass(
|
||||
type,
|
||||
"element",
|
||||
ALWAYS_LOADED_TYPES,
|
||||
LAZY_LOAD_TYPES
|
||||
);
|
|
@ -0,0 +1,167 @@
|
|||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
any,
|
||||
array,
|
||||
assert,
|
||||
literal,
|
||||
object,
|
||||
optional,
|
||||
string,
|
||||
} from "superstruct";
|
||||
import { HASSDomEvent, fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import { LovelacePictureElementEditor } from "../../../types";
|
||||
import {
|
||||
ConditionalElementConfig,
|
||||
LovelaceElementConfig,
|
||||
} from "../../../elements/types";
|
||||
import "../../conditions/ha-card-conditions-editor";
|
||||
import "../../hui-picture-elements-card-row-editor";
|
||||
import { LovelaceCardConfig } from "../../../../../data/lovelace/config/card";
|
||||
import { EditSubElementEvent, SubElementEditorConfig } from "../../types";
|
||||
import "../../hui-sub-element-editor";
|
||||
import { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||
|
||||
const conditionalElementConfigStruct = object({
|
||||
type: literal("conditional"),
|
||||
conditions: optional(array(any())),
|
||||
elements: optional(array(any())),
|
||||
title: optional(string()),
|
||||
});
|
||||
|
||||
const SCHEMA = [{ name: "title", selector: { text: {} } }] as const;
|
||||
|
||||
@customElement("hui-conditional-element-editor")
|
||||
export class HuiConditionalElementEditor
|
||||
extends LitElement
|
||||
implements LovelacePictureElementEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: ConditionalElementConfig;
|
||||
|
||||
@state() private _subElementEditorConfig?: SubElementEditorConfig;
|
||||
|
||||
public setConfig(config: ConditionalElementConfig): void {
|
||||
assert(config, conditionalElementConfigStruct);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
if (this._subElementEditorConfig) {
|
||||
return html`
|
||||
<hui-sub-element-editor
|
||||
.hass=${this.hass}
|
||||
.config=${this._subElementEditorConfig}
|
||||
@go-back=${this._goBack}
|
||||
@config-changed=${this._handleSubElementChanged}
|
||||
>
|
||||
</hui-sub-element-editor>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._formChanged}
|
||||
></ha-form>
|
||||
<ha-card-conditions-editor
|
||||
.hass=${this.hass}
|
||||
.conditions=${this._config.conditions || []}
|
||||
@value-changed=${this._conditionChanged}
|
||||
>
|
||||
</ha-card-conditions-editor>
|
||||
<hui-picture-elements-card-row-editor
|
||||
.hass=${this.hass}
|
||||
.elements=${this._config.elements || []}
|
||||
@elements-changed=${this._elementsChanged}
|
||||
@edit-detail-element=${this._editDetailElement}
|
||||
></hui-picture-elements-card-row-editor>
|
||||
`;
|
||||
}
|
||||
|
||||
private _formChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
|
||||
private _conditionChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
if (!this._config) {
|
||||
return;
|
||||
}
|
||||
const conditions = ev.detail.value;
|
||||
this._config = { ...this._config, conditions };
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _elementsChanged(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
|
||||
const config = {
|
||||
...this._config,
|
||||
elements: ev.detail.elements as LovelaceElementConfig[],
|
||||
} as LovelaceCardConfig;
|
||||
|
||||
fireEvent(this, "config-changed", { config });
|
||||
}
|
||||
|
||||
private _handleSubElementChanged(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const configValue = this._subElementEditorConfig?.type;
|
||||
const value = ev.detail.config;
|
||||
|
||||
if (configValue === "element") {
|
||||
const newConfigElements = this._config.elements!.concat();
|
||||
if (!value) {
|
||||
newConfigElements.splice(this._subElementEditorConfig!.index!, 1);
|
||||
this._goBack();
|
||||
} else {
|
||||
newConfigElements[this._subElementEditorConfig!.index!] = value;
|
||||
}
|
||||
|
||||
this._config = { ...this._config!, elements: newConfigElements };
|
||||
}
|
||||
|
||||
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(ev?): void {
|
||||
ev?.stopPropagation();
|
||||
this._subElementEditorConfig = undefined;
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) =>
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(`ui.panel.lovelace.editor.elements.${schema.name}`) ||
|
||||
schema.name;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-conditional-element-editor": HuiConditionalElementEditor;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { any, assert, literal, object, optional, string } from "superstruct";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import { LovelacePictureElementEditor } from "../../../types";
|
||||
import { IconElementConfig } from "../../../elements/types";
|
||||
import { actionConfigStruct } from "../../structs/action-struct";
|
||||
|
||||
const iconElementConfigStruct = object({
|
||||
type: literal("icon"),
|
||||
entity: optional(string()),
|
||||
icon: optional(string()),
|
||||
style: optional(any()),
|
||||
title: optional(string()),
|
||||
tap_action: optional(actionConfigStruct),
|
||||
hold_action: optional(actionConfigStruct),
|
||||
double_tap_action: optional(actionConfigStruct),
|
||||
});
|
||||
|
||||
const SCHEMA = [
|
||||
{ name: "icon", selector: { icon: {} } },
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{ name: "entity", selector: { entity: {} } },
|
||||
{
|
||||
name: "tap_action",
|
||||
selector: {
|
||||
ui_action: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hold_action",
|
||||
selector: {
|
||||
ui_action: {},
|
||||
},
|
||||
},
|
||||
{ name: "style", selector: { object: {} } },
|
||||
] as const;
|
||||
|
||||
@customElement("hui-icon-element-editor")
|
||||
export class HuiIconElementEditor
|
||||
extends LitElement
|
||||
implements LovelacePictureElementEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: IconElementConfig;
|
||||
|
||||
public setConfig(config: IconElementConfig): void {
|
||||
assert(config, iconElementConfigStruct);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) =>
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(`ui.panel.lovelace.editor.elements.${schema.name}`) ||
|
||||
schema.name;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-icon-element-editor": HuiIconElementEditor;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { any, assert, literal, object, optional, string } from "superstruct";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import { LovelacePictureElementEditor } from "../../../types";
|
||||
import { ImageElementConfig } from "../../../elements/types";
|
||||
import { actionConfigStruct } from "../../structs/action-struct";
|
||||
|
||||
const imageElementConfigStruct = object({
|
||||
type: literal("image"),
|
||||
entity: optional(string()),
|
||||
image: optional(string()),
|
||||
style: optional(any()),
|
||||
title: optional(string()),
|
||||
tap_action: optional(actionConfigStruct),
|
||||
hold_action: optional(actionConfigStruct),
|
||||
double_tap_action: optional(actionConfigStruct),
|
||||
camera_image: optional(string()),
|
||||
camera_view: optional(string()),
|
||||
state_image: optional(any()),
|
||||
filter: optional(string()),
|
||||
state_filter: optional(any()),
|
||||
aspect_ratio: optional(string()),
|
||||
});
|
||||
|
||||
const SCHEMA = [
|
||||
{ name: "entity", selector: { entity: {} } },
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{
|
||||
name: "tap_action",
|
||||
selector: {
|
||||
ui_action: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hold_action",
|
||||
selector: {
|
||||
ui_action: {},
|
||||
},
|
||||
},
|
||||
{ name: "image", selector: { text: {} } },
|
||||
{ name: "camera_image", selector: { entity: { domain: "camera" } } },
|
||||
{
|
||||
name: "camera_view",
|
||||
selector: { select: { options: ["auto", "live"] } },
|
||||
},
|
||||
{ name: "state_image", selector: { object: {} } },
|
||||
{ name: "filter", selector: { text: {} } },
|
||||
{ name: "state_filter", selector: { object: {} } },
|
||||
{ name: "aspect_ratio", selector: { text: {} } },
|
||||
{ name: "style", selector: { object: {} } },
|
||||
] as const;
|
||||
|
||||
@customElement("hui-image-element-editor")
|
||||
export class HuiImageElementEditor
|
||||
extends LitElement
|
||||
implements LovelacePictureElementEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: ImageElementConfig;
|
||||
|
||||
public setConfig(config: ImageElementConfig): void {
|
||||
assert(config, imageElementConfigStruct);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) =>
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(`ui.panel.lovelace.editor.elements.${schema.name}`) ||
|
||||
schema.name;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-image-element-editor": HuiImageElementEditor;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { any, assert, literal, object, optional, string } from "superstruct";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import { LovelacePictureElementEditor } from "../../../types";
|
||||
import { ServiceButtonElementConfig } from "../../../elements/types";
|
||||
// import { UiAction } from "../../components/hui-action-editor";
|
||||
|
||||
const serviceButtonElementConfigStruct = object({
|
||||
type: literal("service-button"),
|
||||
style: optional(any()),
|
||||
title: optional(string()),
|
||||
service: optional(string()),
|
||||
service_data: optional(any()),
|
||||
});
|
||||
|
||||
const SCHEMA = [
|
||||
{ name: "title", selector: { text: {} } },
|
||||
/* {
|
||||
name: "service",
|
||||
selector: {
|
||||
ui_action: { actions: ["call-service"] as UiAction[] },
|
||||
},
|
||||
}, */
|
||||
{ name: "service", selector: { text: {} } },
|
||||
{ name: "service_data", selector: { object: {} } },
|
||||
{ name: "style", selector: { object: {} } },
|
||||
] as const;
|
||||
|
||||
@customElement("hui-service-button-element-editor")
|
||||
export class HuiServiceButtonElementEditor
|
||||
extends LitElement
|
||||
implements LovelacePictureElementEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: ServiceButtonElementConfig;
|
||||
|
||||
public setConfig(config: ServiceButtonElementConfig): void {
|
||||
assert(config, serviceButtonElementConfigStruct);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) =>
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(`ui.panel.lovelace.editor.elements.${schema.name}`) ||
|
||||
schema.name;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-service-button-element-editor": HuiServiceButtonElementEditor;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { any, assert, literal, object, optional, string } from "superstruct";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import { LovelacePictureElementEditor } from "../../../types";
|
||||
import { StateBadgeElementConfig } from "../../../elements/types";
|
||||
import { actionConfigStruct } from "../../structs/action-struct";
|
||||
|
||||
const stateBadgeElementConfigStruct = object({
|
||||
type: literal("state-badge"),
|
||||
entity: optional(string()),
|
||||
style: optional(any()),
|
||||
title: optional(string()),
|
||||
tap_action: optional(actionConfigStruct),
|
||||
hold_action: optional(actionConfigStruct),
|
||||
double_tap_action: optional(actionConfigStruct),
|
||||
});
|
||||
|
||||
const SCHEMA = [
|
||||
{ name: "entity", selector: { entity: {} } },
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{
|
||||
name: "tap_action",
|
||||
selector: {
|
||||
ui_action: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hold_action",
|
||||
selector: {
|
||||
ui_action: {},
|
||||
},
|
||||
},
|
||||
{ name: "style", selector: { object: {} } },
|
||||
] as const;
|
||||
|
||||
@customElement("hui-state-badge-element-editor")
|
||||
export class HuiStateBadgeElementEditor
|
||||
extends LitElement
|
||||
implements LovelacePictureElementEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: StateBadgeElementConfig;
|
||||
|
||||
public setConfig(config: StateBadgeElementConfig): void {
|
||||
assert(config, stateBadgeElementConfigStruct);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) =>
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(`ui.panel.lovelace.editor.elements.${schema.name}`) ||
|
||||
schema.name;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-state-badge-element-editor": HuiStateBadgeElementEditor;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
any,
|
||||
assert,
|
||||
boolean,
|
||||
literal,
|
||||
object,
|
||||
optional,
|
||||
string,
|
||||
} from "superstruct";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import { LovelacePictureElementEditor } from "../../../types";
|
||||
import { StateIconElementConfig } from "../../../elements/types";
|
||||
import { actionConfigStruct } from "../../structs/action-struct";
|
||||
|
||||
const stateIconElementConfigStruct = object({
|
||||
type: literal("state-icon"),
|
||||
entity: optional(string()),
|
||||
icon: optional(string()),
|
||||
state_color: optional(boolean()),
|
||||
style: optional(any()),
|
||||
title: optional(string()),
|
||||
tap_action: optional(actionConfigStruct),
|
||||
hold_action: optional(actionConfigStruct),
|
||||
double_tap_action: optional(actionConfigStruct),
|
||||
});
|
||||
|
||||
const SCHEMA = [
|
||||
{ name: "entity", selector: { entity: {} } },
|
||||
{ name: "icon", selector: { icon: {} } },
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{ name: "state_color", default: true, selector: { boolean: {} } },
|
||||
{
|
||||
name: "tap_action",
|
||||
selector: {
|
||||
ui_action: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hold_action",
|
||||
selector: {
|
||||
ui_action: {},
|
||||
},
|
||||
},
|
||||
{ name: "style", selector: { object: {} } },
|
||||
] as const;
|
||||
|
||||
@customElement("hui-state-icon-element-editor")
|
||||
export class HuiStateIconElementEditor
|
||||
extends LitElement
|
||||
implements LovelacePictureElementEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: StateIconElementConfig;
|
||||
|
||||
public setConfig(config: StateIconElementConfig): void {
|
||||
assert(config, stateIconElementConfigStruct);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) =>
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(`ui.panel.lovelace.editor.elements.${schema.name}`) ||
|
||||
schema.name;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-state-icon-element-editor": HuiStateIconElementEditor;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { any, assert, literal, object, optional, string } from "superstruct";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import { LovelacePictureElementEditor } from "../../../types";
|
||||
import { StateLabelElementConfig } from "../../../elements/types";
|
||||
import { actionConfigStruct } from "../../structs/action-struct";
|
||||
|
||||
const stateLabelElementConfigStruct = object({
|
||||
type: literal("state-label"),
|
||||
entity: optional(string()),
|
||||
attribute: optional(string()),
|
||||
prefix: optional(string()),
|
||||
suffix: optional(string()),
|
||||
style: optional(any()),
|
||||
title: optional(string()),
|
||||
tap_action: optional(actionConfigStruct),
|
||||
hold_action: optional(actionConfigStruct),
|
||||
double_tap_action: optional(actionConfigStruct),
|
||||
});
|
||||
|
||||
const SCHEMA = [
|
||||
{ name: "entity", required: true, selector: { entity: {} } },
|
||||
{
|
||||
name: "attribute",
|
||||
selector: { attribute: {} },
|
||||
context: {
|
||||
filter_entity: "entity",
|
||||
},
|
||||
},
|
||||
{ name: "prefix", selector: { text: {} } },
|
||||
{ name: "suffix", selector: { text: {} } },
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{
|
||||
name: "tap_action",
|
||||
selector: {
|
||||
ui_action: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hold_action",
|
||||
selector: {
|
||||
ui_action: {},
|
||||
},
|
||||
},
|
||||
{ name: "style", selector: { object: {} } },
|
||||
] as const;
|
||||
|
||||
@customElement("hui-state-label-element-editor")
|
||||
export class HuiStateLabelElementEditor
|
||||
extends LitElement
|
||||
implements LovelacePictureElementEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: StateLabelElementConfig;
|
||||
|
||||
public setConfig(config: StateLabelElementConfig): void {
|
||||
assert(config, stateLabelElementConfigStruct);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) =>
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(`ui.panel.lovelace.editor.elements.${schema.name}`) ||
|
||||
schema.name;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-state-label-element-editor": HuiStateLabelElementEditor;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
import { CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
any,
|
||||
array,
|
||||
assert,
|
||||
assign,
|
||||
object,
|
||||
optional,
|
||||
string,
|
||||
type,
|
||||
} from "superstruct";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import "../../../../components/ha-icon";
|
||||
import "../../../../components/ha-switch";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { PictureElementsCardConfig } from "../../cards/types";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import "../hui-sub-element-editor";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import { EditSubElementEvent, SubElementEditorConfig } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
import "../hui-picture-elements-card-row-editor";
|
||||
import { LovelaceElementConfig } from "../../elements/types";
|
||||
import { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
|
||||
import { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
|
||||
const genericElementConfigStruct = type({
|
||||
type: string(),
|
||||
});
|
||||
|
||||
const cardConfigStruct = assign(
|
||||
baseLovelaceCardConfig,
|
||||
object({
|
||||
image: optional(string()),
|
||||
camera_image: optional(string()),
|
||||
camera_view: optional(string()),
|
||||
elements: array(genericElementConfigStruct),
|
||||
title: optional(string()),
|
||||
state_filter: optional(any()),
|
||||
theme: optional(string()),
|
||||
dark_mode_image: optional(string()),
|
||||
dark_mode_filter: optional(any()),
|
||||
})
|
||||
);
|
||||
|
||||
@customElement("hui-picture-elements-card-editor")
|
||||
export class HuiPictureElementsCardEditor
|
||||
extends LitElement
|
||||
implements LovelaceCardEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: PictureElementsCardConfig;
|
||||
|
||||
@state() private _subElementEditorConfig?: SubElementEditorConfig;
|
||||
|
||||
public setConfig(config: PictureElementsCardConfig): void {
|
||||
assert(config, cardConfigStruct);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(localize: LocalizeFunc) =>
|
||||
[
|
||||
{
|
||||
name: "",
|
||||
type: "expandable",
|
||||
title: localize(
|
||||
"ui.panel.lovelace.editor.card.picture-elements.card_options"
|
||||
),
|
||||
schema: [
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{ name: "image", selector: { text: {} } },
|
||||
{ name: "dark_mode_image", selector: { text: {} } },
|
||||
{
|
||||
name: "camera_image",
|
||||
selector: { entity: { domain: "camera" } },
|
||||
},
|
||||
{
|
||||
name: "camera_view",
|
||||
selector: { select: { options: ["auto", "live"] } },
|
||||
},
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
{ name: "state_filter", selector: { object: {} } },
|
||||
{ name: "dark_mode_filter", selector: { object: {} } },
|
||||
],
|
||||
},
|
||||
] as const
|
||||
);
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
if (this._subElementEditorConfig) {
|
||||
return html`
|
||||
<hui-sub-element-editor
|
||||
.hass=${this.hass}
|
||||
.config=${this._subElementEditorConfig}
|
||||
@go-back=${this._goBack}
|
||||
@config-changed=${this._handleSubElementChanged}
|
||||
>
|
||||
</hui-sub-element-editor>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${this._schema(this.hass.localize)}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._formChanged}
|
||||
></ha-form>
|
||||
<hui-picture-elements-card-row-editor
|
||||
.hass=${this.hass}
|
||||
.elements=${this._config.elements}
|
||||
@elements-changed=${this._elementsChanged}
|
||||
@edit-detail-element=${this._editDetailElement}
|
||||
></hui-picture-elements-card-row-editor>
|
||||
`;
|
||||
}
|
||||
|
||||
private _formChanged(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
|
||||
private _elementsChanged(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
|
||||
const config = {
|
||||
...this._config,
|
||||
elements: ev.detail.elements as LovelaceElementConfig[],
|
||||
} as LovelaceCardConfig;
|
||||
|
||||
fireEvent(this, "config-changed", { config });
|
||||
}
|
||||
|
||||
private _handleSubElementChanged(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const configValue = this._subElementEditorConfig?.type;
|
||||
const value = ev.detail.config;
|
||||
|
||||
if (configValue === "element") {
|
||||
const newConfigElements = this._config.elements!.concat();
|
||||
if (!value) {
|
||||
newConfigElements.splice(this._subElementEditorConfig!.index!, 1);
|
||||
this._goBack();
|
||||
} else {
|
||||
newConfigElements[this._subElementEditorConfig!.index!] = value;
|
||||
}
|
||||
|
||||
this._config = { ...this._config!, elements: newConfigElements };
|
||||
}
|
||||
|
||||
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 {
|
||||
this._subElementEditorConfig = undefined;
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema) => {
|
||||
switch (schema.name) {
|
||||
case "dark_mode_image":
|
||||
case "state_filter":
|
||||
case "dark_mode_filter":
|
||||
return (
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.picture-elements.${schema.name}`
|
||||
) || schema.name
|
||||
);
|
||||
default:
|
||||
return (
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) || schema.name
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [configElementStyle];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-picture-elements-card-editor": HuiPictureElementsCardEditor;
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import type { HomeAssistant } from "../../../types";
|
|||
import type { LovelaceRowConfig } from "../entity-rows/types";
|
||||
import { LovelaceHeaderFooterConfig } from "../header-footer/types";
|
||||
import { LovelaceCardFeatureConfig } from "../card-features/types";
|
||||
import { LovelaceElementConfig } from "../elements/types";
|
||||
import type {
|
||||
LovelaceConfigForm,
|
||||
LovelaceGenericElementEditor,
|
||||
|
@ -38,7 +39,8 @@ export interface ConfigChangedEvent {
|
|||
| LovelaceRowConfig
|
||||
| LovelaceHeaderFooterConfig
|
||||
| LovelaceCardFeatureConfig
|
||||
| LovelaceStrategyConfig;
|
||||
| LovelaceStrategyConfig
|
||||
| LovelaceElementConfig;
|
||||
error?: string;
|
||||
guiModeAvailable?: boolean;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,251 @@
|
|||
import { mdiClose, mdiPencil, mdiContentDuplicate } from "@mdi/js";
|
||||
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "../../../components/ha-select";
|
||||
import type { HaSelect } from "../../../components/ha-select";
|
||||
import {
|
||||
ConditionalElementConfig,
|
||||
IconElementConfig,
|
||||
ImageElementConfig,
|
||||
LovelaceElementConfig,
|
||||
ServiceButtonElementConfig,
|
||||
StateBadgeElementConfig,
|
||||
StateIconElementConfig,
|
||||
StateLabelElementConfig,
|
||||
} from "../elements/types";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"elements-changed": {
|
||||
elements: any[];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const elementTypes: string[] = [
|
||||
"state-badge",
|
||||
"state-icon",
|
||||
"state-label",
|
||||
"service-button",
|
||||
"icon",
|
||||
"image",
|
||||
"conditional",
|
||||
];
|
||||
|
||||
@customElement("hui-picture-elements-card-row-editor")
|
||||
export class HuiPictureElementsCardRowEditor extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public elements?: LovelaceElementConfig[];
|
||||
|
||||
@query("ha-select") private _select!: HaSelect;
|
||||
|
||||
protected render() {
|
||||
if (!this.elements || !this.hass) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.picture-elements.elements"
|
||||
)}
|
||||
</h3>
|
||||
<div class="elements">
|
||||
${this.elements.map(
|
||||
(element, index) => html`
|
||||
<div class="element">
|
||||
${element.type
|
||||
? html`
|
||||
<div class="element-row">
|
||||
<div>
|
||||
<span>
|
||||
${this.hass?.localize(
|
||||
`ui.panel.lovelace.editor.card.picture-elements.element_types.${element.type}`
|
||||
) || element.type}
|
||||
</span>
|
||||
<span class="secondary"
|
||||
>${this._getSecondaryDescription(element)}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
<ha-icon-button
|
||||
.label=${this.hass!.localize("ui.common.clear")}
|
||||
.path=${mdiClose}
|
||||
class="remove-icon"
|
||||
.index=${index}
|
||||
@click=${this._removeRow}
|
||||
></ha-icon-button>
|
||||
<ha-icon-button
|
||||
.label=${this.hass!.localize("ui.common.edit")}
|
||||
.path=${mdiPencil}
|
||||
class="edit-icon"
|
||||
.index=${index}
|
||||
@click=${this._editRow}
|
||||
></ha-icon-button>
|
||||
<ha-icon-button
|
||||
.label=${"ui.common.duplicate"}
|
||||
.path=${mdiContentDuplicate}
|
||||
class="duplicate-icon"
|
||||
.index=${index}
|
||||
@click=${this._duplicateRow}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
<ha-select
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.picture-elements.new_element"
|
||||
)}
|
||||
.value=${""}
|
||||
@closed=${stopPropagation}
|
||||
@selected=${this._addElement}
|
||||
>
|
||||
${elementTypes.map(
|
||||
(element) => html`
|
||||
<mwc-list-item .value=${element}
|
||||
>${this.hass?.localize(
|
||||
`ui.panel.lovelace.editor.card.picture-elements.element_types.${element}`
|
||||
)}</mwc-list-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _getSecondaryDescription(element: LovelaceElementConfig): string {
|
||||
switch (element.type) {
|
||||
case "icon":
|
||||
return element.title ?? (element as IconElementConfig).icon ?? "";
|
||||
case "state-badge":
|
||||
case "state-icon":
|
||||
case "state-label":
|
||||
return (
|
||||
element.title ??
|
||||
(
|
||||
element as
|
||||
| StateBadgeElementConfig
|
||||
| StateIconElementConfig
|
||||
| StateLabelElementConfig
|
||||
).entity ??
|
||||
""
|
||||
);
|
||||
case "service-button":
|
||||
return (
|
||||
element.title ?? (element as ServiceButtonElementConfig).service ?? ""
|
||||
);
|
||||
case "image":
|
||||
return (
|
||||
element.title ??
|
||||
(element as ImageElementConfig).image ??
|
||||
(element as ImageElementConfig).camera_image ??
|
||||
""
|
||||
);
|
||||
case "conditional":
|
||||
return (
|
||||
element.title ??
|
||||
`${((element as ConditionalElementConfig).elements || []).length.toString()} ${this.hass?.localize("ui.panel.lovelace.editor.card.picture-elements.elements")}`
|
||||
);
|
||||
}
|
||||
return "Unknown type";
|
||||
}
|
||||
|
||||
private async _addElement(ev): Promise<void> {
|
||||
const value = ev.target!.value;
|
||||
if (value === "") {
|
||||
return;
|
||||
}
|
||||
const newElements = this.elements!.concat({
|
||||
type: value! as string,
|
||||
...(value !== "conditional"
|
||||
? {
|
||||
style: {
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
} as LovelaceElementConfig);
|
||||
fireEvent(this, "elements-changed", { elements: newElements });
|
||||
this._select.select(-1);
|
||||
}
|
||||
|
||||
private _removeRow(ev: CustomEvent): void {
|
||||
const index = (ev.currentTarget as any).index;
|
||||
const newElements = this.elements!.concat();
|
||||
|
||||
newElements.splice(index, 1);
|
||||
|
||||
fireEvent(this, "elements-changed", { elements: newElements });
|
||||
}
|
||||
|
||||
private _editRow(ev: CustomEvent): void {
|
||||
const index = (ev.currentTarget as any).index;
|
||||
fireEvent(this, "edit-detail-element", {
|
||||
subElementConfig: {
|
||||
index,
|
||||
type: "element",
|
||||
elementConfig: this.elements![index],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _duplicateRow(ev: CustomEvent): void {
|
||||
const index = (ev.currentTarget as any).index;
|
||||
const newElements = [...this.elements!, this.elements![index]];
|
||||
|
||||
fireEvent(this, "elements-changed", { elements: newElements });
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
.element {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.element-row {
|
||||
height: 60px;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.element-row div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.remove-icon,
|
||||
.edit-icon,
|
||||
.duplicate-icon {
|
||||
--mdc-icon-button-size: 36px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.secondary {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-picture-elements-card-row-editor": HuiPictureElementsCardRowEditor;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,13 @@
|
|||
import "@material/mwc-button";
|
||||
import { mdiCodeBraces, mdiListBoxOutline } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
nothing,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-icon-button";
|
||||
|
@ -13,6 +20,7 @@ import "./header-footer-editor/hui-header-footer-element-editor";
|
|||
import type { HuiElementEditor } from "./hui-element-editor";
|
||||
import "./feature-editor/hui-card-feature-element-editor";
|
||||
import type { GUIModeChangedEvent, SubElementEditorConfig } from "./types";
|
||||
import "./picture-element-editor/hui-picture-element-element-editor";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
|
@ -95,7 +103,18 @@ export class HuiSubElementEditor extends LitElement {
|
|||
@GUImode-changed=${this._handleGUIModeChanged}
|
||||
></hui-card-feature-element-editor>
|
||||
`
|
||||
: ""}
|
||||
: this.config.type === "element"
|
||||
? html`
|
||||
<hui-picture-element-element-editor
|
||||
class="editor"
|
||||
.hass=${this.hass}
|
||||
.value=${this.config.elementConfig}
|
||||
.context=${this.context}
|
||||
@config-changed=${this._handleConfigChanged}
|
||||
@GUImode-changed=${this._handleGUIModeChanged}
|
||||
></hui-picture-element-element-editor>
|
||||
`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import { customElement } from "lit/decorators";
|
||||
import { LovelaceElementConfig } from "../../elements/types";
|
||||
import type { LovelacePictureElementEditor } from "../../types";
|
||||
import { HuiElementEditor } from "../hui-element-editor";
|
||||
import { getPictureElementClass } from "../../create-element/create-picture-element";
|
||||
|
||||
@customElement("hui-picture-element-element-editor")
|
||||
export class HuiPictureElementElementEditor extends HuiElementEditor<LovelaceElementConfig> {
|
||||
protected get configElementType(): string | undefined {
|
||||
return this.value?.type;
|
||||
}
|
||||
|
||||
protected async getConfigElement(): Promise<
|
||||
LovelacePictureElementEditor | undefined
|
||||
> {
|
||||
const elClass = await getPictureElementClass(this.configElementType!);
|
||||
|
||||
// Check if a GUI editor exists
|
||||
if (elClass && elClass.getConfigElement) {
|
||||
return elClass.getConfigElement();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-picture-element-element-editor": HuiPictureElementElementEditor;
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import {
|
|||
import { EntityConfig, LovelaceRowConfig } from "../entity-rows/types";
|
||||
import { LovelaceHeaderFooterConfig } from "../header-footer/types";
|
||||
import { LovelaceCardFeatureConfig } from "../card-features/types";
|
||||
import { LovelaceElementConfig } from "../elements/types";
|
||||
|
||||
export interface YamlChangedEvent extends Event {
|
||||
detail: {
|
||||
|
@ -79,8 +80,9 @@ export interface SubElementEditorConfig {
|
|||
elementConfig?:
|
||||
| LovelaceRowConfig
|
||||
| LovelaceHeaderFooterConfig
|
||||
| LovelaceCardFeatureConfig;
|
||||
type: "header" | "footer" | "row" | "feature";
|
||||
| LovelaceCardFeatureConfig
|
||||
| LovelaceElementConfig;
|
||||
type: "header" | "footer" | "row" | "feature" | "element";
|
||||
}
|
||||
|
||||
export interface EditSubElementEvent {
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
checkConditionsMet,
|
||||
validateConditionalConfig,
|
||||
} from "../common/validate-condition";
|
||||
import { LovelacePictureElementEditor } from "../types";
|
||||
import {
|
||||
ConditionalElementConfig,
|
||||
LovelaceElement,
|
||||
|
@ -11,6 +12,13 @@ import {
|
|||
} from "./types";
|
||||
|
||||
class HuiConditionalElement extends HTMLElement implements LovelaceElement {
|
||||
public static async getConfigElement(): Promise<LovelacePictureElementEditor> {
|
||||
await import(
|
||||
"../editor/config-elements/elements/hui-conditional-element-editor"
|
||||
);
|
||||
return document.createElement("hui-conditional-element-editor");
|
||||
}
|
||||
|
||||
public _hass?: HomeAssistant;
|
||||
|
||||
private _config?: ConditionalElementConfig;
|
||||
|
|
|
@ -8,10 +8,16 @@ import { actionHandler } from "../common/directives/action-handler-directive";
|
|||
import { handleAction } from "../common/handle-action";
|
||||
import { hasAction } from "../common/has-action";
|
||||
import { IconElementConfig, LovelaceElement } from "./types";
|
||||
import { LovelacePictureElementEditor } from "../types";
|
||||
import { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||
|
||||
@customElement("hui-icon-element")
|
||||
export class HuiIconElement extends LitElement implements LovelaceElement {
|
||||
public static async getConfigElement(): Promise<LovelacePictureElementEditor> {
|
||||
await import("../editor/config-elements/elements/hui-icon-element-editor");
|
||||
return document.createElement("hui-icon-element-editor");
|
||||
}
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: IconElementConfig;
|
||||
|
|
|
@ -10,9 +10,15 @@ import { handleAction } from "../common/handle-action";
|
|||
import { hasAction } from "../common/has-action";
|
||||
import "../components/hui-image";
|
||||
import { ImageElementConfig, LovelaceElement } from "./types";
|
||||
import { LovelacePictureElementEditor } from "../types";
|
||||
|
||||
@customElement("hui-image-element")
|
||||
export class HuiImageElement extends LitElement implements LovelaceElement {
|
||||
public static async getConfigElement(): Promise<LovelacePictureElementEditor> {
|
||||
await import("../editor/config-elements/elements/hui-image-element-editor");
|
||||
return document.createElement("hui-image-element-editor");
|
||||
}
|
||||
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: ImageElementConfig;
|
||||
|
|
|
@ -3,12 +3,20 @@ import { customElement, state } from "lit/decorators";
|
|||
import "../../../components/buttons/ha-call-service-button";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceElement, ServiceButtonElementConfig } from "./types";
|
||||
import { LovelacePictureElementEditor } from "../types";
|
||||
|
||||
@customElement("hui-service-button-element")
|
||||
export class HuiServiceButtonElement
|
||||
extends LitElement
|
||||
implements LovelaceElement
|
||||
{
|
||||
public static async getConfigElement(): Promise<LovelacePictureElementEditor> {
|
||||
await import(
|
||||
"../editor/config-elements/elements/hui-service-button-element-editor"
|
||||
);
|
||||
return document.createElement("hui-service-button-element-editor");
|
||||
}
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: ServiceButtonElementConfig;
|
||||
|
|
|
@ -12,12 +12,20 @@ import { hasConfigOrEntityChanged } from "../common/has-changed";
|
|||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||
import "../components/hui-warning-element";
|
||||
import { LovelaceElement, StateBadgeElementConfig } from "./types";
|
||||
import { LovelacePictureElementEditor } from "../types";
|
||||
|
||||
@customElement("hui-state-badge-element")
|
||||
export class HuiStateBadgeElement
|
||||
extends LitElement
|
||||
implements LovelaceElement
|
||||
{
|
||||
public static async getConfigElement(): Promise<LovelacePictureElementEditor> {
|
||||
await import(
|
||||
"../editor/config-elements/elements/hui-state-badge-element-editor"
|
||||
);
|
||||
return document.createElement("hui-state-badge-element-editor");
|
||||
}
|
||||
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: StateBadgeElementConfig;
|
||||
|
@ -44,7 +52,7 @@ export class HuiStateBadgeElement
|
|||
if (!stateObj) {
|
||||
return html`
|
||||
<hui-warning-element
|
||||
.label=${createEntityNotFoundWarning(this.hass, this._config.entity)}
|
||||
.label=${createEntityNotFoundWarning(this.hass, this._config.entity!)}
|
||||
></hui-warning-element>
|
||||
`;
|
||||
}
|
||||
|
|
|
@ -18,10 +18,18 @@ import { hasConfigOrEntityChanged } from "../common/has-changed";
|
|||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||
import "../components/hui-warning-element";
|
||||
import { LovelaceElement, StateIconElementConfig } from "./types";
|
||||
import { LovelacePictureElementEditor } from "../types";
|
||||
import { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||
|
||||
@customElement("hui-state-icon-element")
|
||||
export class HuiStateIconElement extends LitElement implements LovelaceElement {
|
||||
public static async getConfigElement(): Promise<LovelacePictureElementEditor> {
|
||||
await import(
|
||||
"../editor/config-elements/elements/hui-state-icon-element-editor"
|
||||
);
|
||||
return document.createElement("hui-state-icon-element-editor");
|
||||
}
|
||||
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: StateIconElementConfig;
|
||||
|
@ -52,7 +60,7 @@ export class HuiStateIconElement extends LitElement implements LovelaceElement {
|
|||
if (!stateObj) {
|
||||
return html`
|
||||
<hui-warning-element
|
||||
.label=${createEntityNotFoundWarning(this.hass, this._config.entity)}
|
||||
.label=${createEntityNotFoundWarning(this.hass, this._config.entity!)}
|
||||
></hui-warning-element>
|
||||
`;
|
||||
}
|
||||
|
|
|
@ -18,9 +18,17 @@ import { hasConfigOrEntityChanged } from "../common/has-changed";
|
|||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||
import "../components/hui-warning-element";
|
||||
import { LovelaceElement, StateLabelElementConfig } from "./types";
|
||||
import { LovelacePictureElementEditor } from "../types";
|
||||
|
||||
@customElement("hui-state-label-element")
|
||||
class HuiStateLabelElement extends LitElement implements LovelaceElement {
|
||||
public static async getConfigElement(): Promise<LovelacePictureElementEditor> {
|
||||
await import(
|
||||
"../editor/config-elements/elements/hui-state-label-element-editor"
|
||||
);
|
||||
return document.createElement("hui-state-label-element-editor");
|
||||
}
|
||||
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: StateLabelElementConfig;
|
||||
|
@ -47,7 +55,7 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement {
|
|||
if (!stateObj) {
|
||||
return html`
|
||||
<hui-warning-element
|
||||
.label=${createEntityNotFoundWarning(this.hass, this._config.entity)}
|
||||
.label=${createEntityNotFoundWarning(this.hass, this._config.entity!)}
|
||||
></hui-warning-element>
|
||||
`;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ export interface LovelaceElement extends HTMLElement {
|
|||
export interface ConditionalElementConfig extends LovelaceElementConfigBase {
|
||||
conditions: Condition[];
|
||||
elements: LovelaceElementConfigBase[];
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface IconElementConfig extends LovelaceElementConfigBase {
|
||||
|
@ -33,7 +34,8 @@ export interface IconElementConfig extends LovelaceElementConfigBase {
|
|||
tap_action?: ActionConfig;
|
||||
hold_action?: ActionConfig;
|
||||
double_tap_action?: ActionConfig;
|
||||
icon: string;
|
||||
icon?: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface ImageElementConfig extends LovelaceElementConfigBase {
|
||||
|
@ -51,6 +53,7 @@ export interface ImageElementConfig extends LovelaceElementConfigBase {
|
|||
filter?: string;
|
||||
state_filter?: string;
|
||||
aspect_ratio?: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface ServiceButtonElementConfig extends LovelaceElementConfigBase {
|
||||
|
@ -60,7 +63,7 @@ export interface ServiceButtonElementConfig extends LovelaceElementConfigBase {
|
|||
}
|
||||
|
||||
export interface StateBadgeElementConfig extends LovelaceElementConfigBase {
|
||||
entity: string;
|
||||
entity?: string;
|
||||
title?: string;
|
||||
tap_action?: ActionConfig;
|
||||
hold_action?: ActionConfig;
|
||||
|
@ -68,20 +71,22 @@ export interface StateBadgeElementConfig extends LovelaceElementConfigBase {
|
|||
}
|
||||
|
||||
export interface StateIconElementConfig extends LovelaceElementConfigBase {
|
||||
entity: string;
|
||||
entity?: string;
|
||||
tap_action?: ActionConfig;
|
||||
hold_action?: ActionConfig;
|
||||
double_tap_action?: ActionConfig;
|
||||
icon?: string;
|
||||
state_color?: boolean;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface StateLabelElementConfig extends LovelaceElementConfigBase {
|
||||
entity: string;
|
||||
entity?: string;
|
||||
attribute?: string;
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
tap_action?: ActionConfig;
|
||||
hold_action?: ActionConfig;
|
||||
double_tap_action?: ActionConfig;
|
||||
title?: string;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import { Constructor, HomeAssistant } from "../../types";
|
|||
import { LovelaceRow, LovelaceRowConfig } from "./entity-rows/types";
|
||||
import { LovelaceHeaderFooterConfig } from "./header-footer/types";
|
||||
import { LovelaceCardFeatureConfig } from "./card-features/types";
|
||||
import { LovelaceElement, LovelaceElementConfig } from "./elements/types";
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line
|
||||
|
@ -90,6 +91,11 @@ export interface LovelaceRowConstructor extends Constructor<LovelaceRow> {
|
|||
getConfigElement?: () => LovelaceRowEditor;
|
||||
}
|
||||
|
||||
export interface LovelaceElementConstructor
|
||||
extends Constructor<LovelaceElement> {
|
||||
getConfigElement?: () => LovelacePictureElementEditor;
|
||||
}
|
||||
|
||||
export interface LovelaceHeaderFooter extends HTMLElement {
|
||||
hass?: HomeAssistant;
|
||||
type: "header" | "footer";
|
||||
|
@ -110,6 +116,11 @@ export interface LovelaceRowEditor extends LovelaceGenericElementEditor {
|
|||
setConfig(config: LovelaceRowConfig): void;
|
||||
}
|
||||
|
||||
export interface LovelacePictureElementEditor
|
||||
extends LovelaceGenericElementEditor {
|
||||
setConfig(config: LovelaceElementConfig): void;
|
||||
}
|
||||
|
||||
export interface LovelaceGenericElementEditor<C = any> extends HTMLElement {
|
||||
hass?: HomeAssistant;
|
||||
lovelace?: LovelaceConfig;
|
||||
|
|
|
@ -5865,7 +5865,22 @@
|
|||
},
|
||||
"picture-elements": {
|
||||
"name": "Picture elements",
|
||||
"description": "The Picture elements card is one of the most versatile types of cards. The cards allow you to position icons or text and even services! On an image based on coordinates."
|
||||
"description": "The Picture elements card is one of the most versatile types of cards. The cards allow you to position icons or text and even services! On an image based on coordinates.",
|
||||
"card_options": "Card Options",
|
||||
"elements": "Elements",
|
||||
"new_element": "Add new element",
|
||||
"dark_mode_image": "Dark mode image path",
|
||||
"state_filter": "State filter",
|
||||
"dark_mode_filter": "Dark mode state filter",
|
||||
"element_types": {
|
||||
"state-badge": "State badge",
|
||||
"state-icon": "State icon",
|
||||
"state-label": "State label",
|
||||
"service-button": "Service call button",
|
||||
"icon": "Icon",
|
||||
"image": "Image",
|
||||
"conditional": "Conditional"
|
||||
}
|
||||
},
|
||||
"picture-entity": {
|
||||
"name": "Picture entity",
|
||||
|
@ -5931,6 +5946,14 @@
|
|||
"twice_daily": "Twice daily"
|
||||
}
|
||||
},
|
||||
"elements": {
|
||||
"style": "Style",
|
||||
"prefix": "Prefix",
|
||||
"suffix": "Suffix",
|
||||
"state_image": "State image",
|
||||
"filter": "Filter",
|
||||
"state_filter": "[%key:ui::panel::lovelace::editor::card::picture-elements::state_filter%]"
|
||||
},
|
||||
"features": {
|
||||
"name": "Features",
|
||||
"not_compatible": "Not compatible",
|
||||
|
@ -6131,7 +6154,8 @@
|
|||
"header": "Header editor",
|
||||
"footer": "Footer editor",
|
||||
"row": "Entity row editor",
|
||||
"feature": "Feature editor"
|
||||
"feature": "Feature editor",
|
||||
"element": "Element editor"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue