Finishing up the editors (#11846)

This commit is contained in:
Zack Barett 2022-02-24 17:35:28 -06:00 committed by GitHub
parent 5ab6121581
commit 0158610d42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 589 additions and 1100 deletions

View File

@ -90,11 +90,9 @@ export class HuiAlarmPanelCardEditor
private _computeLabelCallback = (schema: HaFormSchema) => { private _computeLabelCallback = (schema: HaFormSchema) => {
if (schema.name === "entity") { if (schema.name === "entity") {
return `${this.hass!.localize( return this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.entity" "ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass!.localize( );
"ui.panel.lovelace.editor.card.config.required"
)})`;
} }
if (schema.name === "name") { if (schema.name === "name") {

View File

@ -107,11 +107,9 @@ export class HuiEntityCardEditor
private _computeLabelCallback = (schema: HaFormSchema) => { private _computeLabelCallback = (schema: HaFormSchema) => {
if (schema.name === "entity") { if (schema.name === "entity") {
return `${this.hass!.localize( return this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.entity" "ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass!.localize( );
"ui.panel.lovelace.editor.card.config.required"
)})`;
} }
return this.hass!.localize( return this.hass!.localize(

View File

@ -1,25 +1,18 @@
import "@material/mwc-list/mwc-list-item"; import "../../../../components/ha-form/ha-form";
import "@polymer/paper-input/paper-input"; import type { HassEntity } from "home-assistant-js-websocket";
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { assert } from "superstruct"; import { assert } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { stopPropagation } from "../../../../common/dom/stop_propagation";
import { computeDomain } from "../../../../common/entity/compute_domain"; import { computeDomain } from "../../../../common/entity/compute_domain";
import { domainIcon } from "../../../../common/entity/domain_icon"; import { domainIcon } from "../../../../common/entity/domain_icon";
import "../../../../components/ha-formfield"; import type { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/ha-icon-picker"; import type { HaFormSchema } from "../../../../components/ha-form/types";
import "../../../../components/ha-select"; import type { HomeAssistant } from "../../../../types";
import "../../../../components/ha-switch"; import type { EntitiesCardEntityConfig } from "../../cards/types";
import { HomeAssistant } from "../../../../types"; import type { LovelaceRowEditor } from "../../types";
import { EntitiesCardEntityConfig } from "../../cards/types";
import "../../components/hui-action-editor";
import "../../components/hui-entity-editor";
import "../../components/hui-theme-select-editor";
import { LovelaceRowEditor } from "../../types";
import { entitiesConfigStruct } from "../structs/entities-struct"; import { entitiesConfigStruct } from "../structs/entities-struct";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
const SecondaryInfoValues: { [key: string]: { domains?: string[] } } = { const SecondaryInfoValues: { [key: string]: { domains?: string[] } } = {
"entity-id": {}, "entity-id": {},
@ -45,128 +38,106 @@ export class HuiGenericEntityRowEditor
this._config = config; this._config = config;
} }
get _entity(): string { private _schema = memoizeOne(
return this._config!.entity || ""; (
} entity: string,
icon: string | undefined,
entityState: HassEntity,
localize: LocalizeFunc
): HaFormSchema[] => {
const domain = computeDomain(entity);
get _name(): string { return [
return this._config!.name || ""; { name: "entity", required: true, selector: { entity: {} } },
} {
type: "grid",
get _icon(): string { name: "",
return this._config!.icon || ""; schema: [
} { name: "name", selector: { text: {} } },
{
get _secondary_info(): string { name: "icon",
return this._config!.secondary_info || ""; selector: {
icon: {
placeholder: icon || entityState?.attributes.icon,
fallbackPath:
!icon && !entityState?.attributes.icon && entityState
? domainIcon(domain, entityState)
: undefined,
},
},
},
],
},
{
name: "secondary_info",
selector: {
select: {
options: Object.keys(SecondaryInfoValues)
.filter(
(info) =>
!("domains" in SecondaryInfoValues[info]) ||
("domains" in SecondaryInfoValues[info] &&
SecondaryInfoValues[info].domains!.includes(domain))
)
.map((info) => ({
value: info,
label: localize(
`ui.panel.lovelace.editor.card.entities.secondary_info_values.${info}`
),
})),
},
},
},
];
} }
);
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.hass || !this._config) { if (!this.hass || !this._config) {
return html``; return html``;
} }
const domain = computeDomain(this._entity); const entityState = this.hass.states[this._config.entity];
const entityState = this.hass.states[this._entity];
const schema = this._schema(
this._config.entity,
this._config.icon,
entityState,
this.hass.localize
);
return html` return html`
<div class="card-config"> <ha-form
<ha-entity-picker
allow-custom-entity
.hass=${this.hass} .hass=${this.hass}
.value=${this._config.entity} .data=${this._config}
.configValue=${"entity"} .schema=${schema}
@change=${this._valueChanged} .computeLabel=${this._computeLabelCallback}
></ha-entity-picker>
<div class="side-by-side">
<paper-input
.label=${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.name"
)}
.value=${this._config.name}
.configValue=${"name"}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></paper-input> ></ha-form>
<ha-icon-picker
.label=${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.icon"
)}
.value=${this._config.icon}
.configValue=${"icon"}
.placeholder=${entityState?.attributes.icon}
.fallbackPath=${!this._icon &&
!entityState?.attributes.icon &&
entityState
? domainIcon(computeDomain(entityState.entity_id), entityState)
: undefined}
@value-changed=${this._valueChanged}
></ha-icon-picker>
</div>
<ha-select
label="Secondary Info"
.configValue=${"secondary_info"}
@selected=${this._valueChanged}
@closed=${stopPropagation}
.value=${this._config.secondary_info || "none"}
naturalMenuWidth
fixedMenuPosition
>
<mwc-list-item value=""
>${this.hass!.localize(
"ui.panel.lovelace.editor.card.entities.secondary_info_values.none"
)}</mwc-list-item
>
${Object.keys(SecondaryInfoValues).map((info) => {
if (
!("domains" in SecondaryInfoValues[info]) ||
("domains" in SecondaryInfoValues[info] &&
SecondaryInfoValues[info].domains!.includes(domain))
) {
return html`
<mwc-list-item .value=${info}>
${this.hass!.localize(
`ui.panel.lovelace.editor.card.entities.secondary_info_values.${info}`
)}
</mwc-list-item>
`;
}
return "";
})}
</ha-select>
</div>
`; `;
} }
private _valueChanged(ev: EntitiesEditorEvent): void { private _valueChanged(ev: CustomEvent): void {
if (!this._config || !this.hass) { fireEvent(this, "config-changed", { config: ev.detail.value });
return;
}
const target = ev.target! as EditorTarget;
const value = target.value || ev.detail?.item?.value;
if (this[`_${target.configValue}`] === value) {
return;
} }
if (target.configValue) { private _computeLabelCallback = (schema: HaFormSchema) => {
if (value === "" || !value) { if (schema.name === "entity") {
this._config = { ...this._config }; return this.hass!.localize(
delete this._config[target.configValue!]; "ui.panel.lovelace.editor.card.generic.entity"
} else { );
this._config = { }
...this._config,
[target.configValue!]: value, return (
this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
) ||
this.hass!.localize(
`ui.panel.lovelace.editor.card.entity-row.${schema.name}`
)
);
}; };
} }
}
fireEvent(this, "config-changed", { config: this._config });
}
static get styles(): CSSResultGroup {
return configElementStyle;
}
}
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {

View File

@ -1,16 +1,13 @@
import "@polymer/paper-input/paper-input"; import "../../../../components/ha-form/ha-form";
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { assert, assign, object, optional, string } from "superstruct"; import { assert, assign, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/entity/ha-entity-picker"; import type { HaFormSchema } from "../../../../components/ha-form/types";
import { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
import { HumidifierCardConfig } from "../../cards/types"; import type { HumidifierCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor"; import type { LovelaceCardEditor } from "../../types";
import { LovelaceCardEditor } from "../../types";
import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
const cardConfigStruct = assign( const cardConfigStruct = assign(
baseLovelaceCardConfig, baseLovelaceCardConfig,
@ -21,7 +18,21 @@ const cardConfigStruct = assign(
}) })
); );
const includeDomains = ["humidifier"]; const SCHEMA: HaFormSchema[] = [
{
name: "entity",
required: true,
selector: { entity: { domain: "humidifer" } },
},
{
type: "grid",
name: "",
schema: [
{ name: "name", selector: { text: {} } },
{ name: "theme", selector: { theme: {} } },
],
},
];
@customElement("hui-humidifier-card-editor") @customElement("hui-humidifier-card-editor")
export class HuiHumidifierCardEditor export class HuiHumidifierCardEditor
@ -37,81 +48,37 @@ export class HuiHumidifierCardEditor
this._config = config; this._config = config;
} }
get _entity(): string {
return this._config!.entity || "";
}
get _name(): string {
return this._config!.name || "";
}
get _theme(): string {
return this._config!.theme || "";
}
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.hass || !this._config) { if (!this.hass || !this._config) {
return html``; return html``;
} }
return html` return html`
<div class="card-config"> <ha-form
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass} .hass=${this.hass}
.value=${this._entity} .data=${this._config}
.configValue=${"entity"} .schema=${SCHEMA}
.includeDomains=${includeDomains} .computeLabel=${this._computeLabelCallback}
@change=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></paper-input> ></ha-form>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
`; `;
} }
private _valueChanged(ev: EntitiesEditorEvent): void { private _valueChanged(ev: CustomEvent): void {
if (!this._config || !this.hass) { fireEvent(this, "config-changed", { config: ev.detail.value });
return;
}
const target = ev.target! as EditorTarget;
if (this[`_${target.configValue}`] === target.value) {
return;
}
if (target.configValue) {
if (target.value === "") {
this._config = { ...this._config };
delete this._config[target.configValue!];
} else {
this._config = { ...this._config, [target.configValue!]: target.value };
}
}
fireEvent(this, "config-changed", { config: this._config });
} }
static get styles(): CSSResultGroup { private _computeLabelCallback = (schema: HaFormSchema) => {
return configElementStyle; if (schema.name === "entity") {
return this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.entity"
);
} }
return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
);
};
} }
declare global { declare global {

View File

@ -1,14 +1,13 @@
import "@polymer/paper-input/paper-input"; import "../../../../components/ha-form/ha-form";
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { assert, assign, object, optional, string } from "superstruct"; import { assert, assign, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { HomeAssistant } from "../../../../types"; import type { HaFormSchema } from "../../../../components/ha-form/types";
import { IframeCardConfig } from "../../cards/types"; import type { HomeAssistant } from "../../../../types";
import { LovelaceCardEditor } from "../../types"; import type { IframeCardConfig } from "../../cards/types";
import type { LovelaceCardEditor } from "../../types";
import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
const cardConfigStruct = assign( const cardConfigStruct = assign(
baseLovelaceCardConfig, baseLovelaceCardConfig,
@ -19,6 +18,18 @@ const cardConfigStruct = assign(
}) })
); );
const SCHEMA: HaFormSchema[] = [
{ name: "title", selector: { text: {} } },
{
name: "",
type: "grid",
schema: [
{ name: "url", required: true, selector: { text: {} } },
{ name: "aspect_ratio", selector: { text: {} } },
],
},
];
@customElement("hui-iframe-card-editor") @customElement("hui-iframe-card-editor")
export class HuiIframeCardEditor export class HuiIframeCardEditor
extends LitElement extends LitElement
@ -33,85 +44,28 @@ export class HuiIframeCardEditor
this._config = config; this._config = config;
} }
get _title(): string {
return this._config!.title || "";
}
get _url(): string {
return this._config!.url || "";
}
get _aspect_ratio(): string {
return this._config!.aspect_ratio || "";
}
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.hass || !this._config) { if (!this.hass || !this._config) {
return html``; return html``;
} }
return html` return html`
<div class="card-config"> <ha-form
<paper-input .hass=${this.hass}
.label="${this.hass.localize( .data=${this._config}
"ui.panel.lovelace.editor.card.generic.url" .schema=${SCHEMA}
)} (${this.hass.localize( .computeLabel=${this._computeLabelCallback}
"ui.panel.lovelace.editor.card.config.required"
)})"
.value=${this._url}
.configValue=${"url"}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></paper-input> ></ha-form>
<div class="side-by-side">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._aspect_ratio}
.configValue=${"aspect_ratio"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
</div>
`; `;
} }
private _valueChanged(ev: EntitiesEditorEvent): void { private _valueChanged(ev: CustomEvent): void {
if (!this._config || !this.hass) { fireEvent(this, "config-changed", { config: ev.detail.value });
return;
}
const target = ev.target! as EditorTarget;
const value = target.value;
if (this[`_${target.configValue}`] === value) {
return;
}
if (target.configValue) {
if (value === "") {
this._config = { ...this._config };
delete this._config[target.configValue!];
} else {
this._config = { ...this._config, [target.configValue!]: value };
}
}
fireEvent(this, "config-changed", { config: this._config });
} }
static get styles(): CSSResultGroup { private _computeLabelCallback = (schema: HaFormSchema) =>
return configElementStyle; this.hass!.localize(`ui.panel.lovelace.editor.card.generic.${schema.name}`);
}
} }
declare global { declare global {

View File

@ -1,22 +1,21 @@
import "@polymer/paper-input/paper-input"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { assert, object, optional, string, assign } from "superstruct"; import { assert, object, optional, string, assign } from "superstruct";
import type { HassEntity } from "home-assistant-js-websocket";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-icon-picker"; import type { ActionConfig } from "../../../../data/lovelace";
import { ActionConfig } from "../../../../data/lovelace"; import type { HomeAssistant } from "../../../../types";
import { HomeAssistant } from "../../../../types"; import type { LightCardConfig } from "../../cards/types";
import { LightCardConfig } from "../../cards/types";
import "../../components/hui-action-editor"; import "../../components/hui-action-editor";
import "../../components/hui-entity-editor"; import type { LovelaceCardEditor } from "../../types";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import { actionConfigStruct } from "../structs/action-struct"; import { actionConfigStruct } from "../structs/action-struct";
import { EditorTarget } from "../types"; import type { EditorTarget } from "../types";
import { configElementStyle } from "./config-elements-style"; import { configElementStyle } from "./config-elements-style";
import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { domainIcon } from "../../../../common/entity/domain_icon"; import { domainIcon } from "../../../../common/entity/domain_icon";
import { computeDomain } from "../../../../common/entity/compute_domain"; import { computeDomain } from "../../../../common/entity/compute_domain";
import type { HaFormSchema } from "../../../../components/ha-form/types";
const cardConfigStruct = assign( const cardConfigStruct = assign(
baseLovelaceCardConfig, baseLovelaceCardConfig,
@ -30,8 +29,6 @@ const cardConfigStruct = assign(
}) })
); );
const includeDomains = ["light"];
@customElement("hui-light-card-editor") @customElement("hui-light-card-editor")
export class HuiLightCardEditor export class HuiLightCardEditor
extends LitElement extends LitElement
@ -46,21 +43,39 @@ export class HuiLightCardEditor
this._config = config; this._config = config;
} }
get _name(): string { private _schema = memoizeOne(
return this._config!.name || ""; (
} entity: string,
icon: string | undefined,
get _theme(): string { entityState: HassEntity
return this._config!.theme || ""; ): HaFormSchema[] => [
} {
name: "entity",
get _entity(): string { required: true,
return this._config!.entity || ""; selector: { entity: { domain: "light" } },
} },
{
get _icon(): string { type: "grid",
return this._config!.icon || ""; name: "",
} schema: [
{ name: "name", selector: { text: {} } },
{
name: "icon",
selector: {
icon: {
placeholder: icon || entityState?.attributes.icon,
fallbackPath:
!icon && !entityState?.attributes.icon && entityState
? domainIcon(computeDomain(entity), entityState)
: undefined,
},
},
},
],
},
{ name: "theme", selector: { theme: {} } },
]
);
get _hold_action(): ActionConfig { get _hold_action(): ActionConfig {
return this._config!.hold_action || { action: "more-info" }; return this._config!.hold_action || { action: "more-info" };
@ -84,59 +99,22 @@ export class HuiLightCardEditor
"none", "none",
]; ];
const entityState = this.hass.states[this._entity]; const entityState = this.hass.states[this._config.entity];
const schema = this._schema(
this._config.entity,
this._config.icon,
entityState
);
return html` return html`
<ha-form
.hass=${this.hass}
.data=${this._config}
.schema=${schema}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
></ha-form>
<div class="card-config"> <div class="card-config">
<ha-entity-picker
.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}
@value-changed=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<div class="side-by-side">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged}
></paper-input>
<ha-icon-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.icon"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._icon}
.placeholder=${this._icon || entityState?.attributes.icon}
.fallbackPath=${!this._icon &&
!entityState?.attributes.icon &&
entityState
? domainIcon(computeDomain(entityState.entity_id), entityState)
: undefined}
.configValue=${"icon"}
@value-changed=${this._valueChanged}
></ha-icon-picker>
</div>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
<hui-action-editor <hui-action-editor
.label="${this.hass.localize( .label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.hold_action" "ui.panel.lovelace.editor.card.generic.hold_action"
@ -147,7 +125,7 @@ export class HuiLightCardEditor
.config=${this._hold_action} .config=${this._hold_action}
.actions=${actions} .actions=${actions}
.configValue=${"hold_action"} .configValue=${"hold_action"}
@value-changed=${this._valueChanged} @value-changed=${this._actionChanged}
></hui-action-editor> ></hui-action-editor>
<hui-action-editor <hui-action-editor
@ -160,13 +138,13 @@ export class HuiLightCardEditor
.config=${this._double_tap_action} .config=${this._double_tap_action}
.actions=${actions} .actions=${actions}
.configValue=${"double_tap_action"} .configValue=${"double_tap_action"}
@value-changed=${this._valueChanged} @value-changed=${this._actionChanged}
></hui-action-editor> ></hui-action-editor>
</div> </div>
`; `;
} }
private _valueChanged(ev: CustomEvent): void { private _actionChanged(ev: CustomEvent): void {
if (!this._config || !this.hass) { if (!this._config || !this.hass) {
return; return;
} }
@ -190,9 +168,33 @@ export class HuiLightCardEditor
fireEvent(this, "config-changed", { config: this._config }); fireEvent(this, "config-changed", { config: this._config });
} }
static get styles(): CSSResultGroup { private _valueChanged(ev: CustomEvent): void {
return configElementStyle; fireEvent(this, "config-changed", { config: ev.detail.value });
} }
private _computeLabelCallback = (schema: HaFormSchema) => {
if (schema.name === "entity") {
return this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.entity"
);
}
return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
);
};
static styles: CSSResultGroup = [
configElementStyle,
css`
ha-form,
hui-action-editor {
display: block;
margin-bottom: 24px;
overflow: auto;
}
`,
];
} }
declare global { declare global {

View File

@ -1,5 +1,5 @@
import "@polymer/paper-input/paper-input"; import "../../../../components/ha-form/ha-form";
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { import {
array, array,
@ -12,15 +12,11 @@ import {
} from "superstruct"; } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/entity/ha-entities-picker"; import "../../../../components/entity/ha-entities-picker";
import "../../../../components/entity/ha-entity-picker"; import type { HaFormSchema } from "../../../../components/ha-form/types";
import { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
import { LogbookCardConfig } from "../../cards/types"; import type { LogbookCardConfig } from "../../cards/types";
import "../../components/hui-entity-editor"; import type { LovelaceCardEditor } from "../../types";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { EditorTarget } from "../types";
import { configElementStyle } from "./config-elements-style";
const cardConfigStruct = assign( const cardConfigStruct = assign(
baseLovelaceCardConfig, baseLovelaceCardConfig,
@ -32,6 +28,18 @@ const cardConfigStruct = assign(
}) })
); );
const SCHEMA: HaFormSchema[] = [
{ name: "title", selector: { text: {} } },
{
name: "",
type: "grid",
schema: [
{ name: "theme", selector: { theme: {} } },
{ name: "hours_to_show", selector: { number: { mode: "box", min: 1 } } },
],
},
];
@customElement("hui-logbook-card-editor") @customElement("hui-logbook-card-editor")
export class HuiLogbookCardEditor export class HuiLogbookCardEditor
extends LitElement extends LitElement
@ -41,67 +49,28 @@ export class HuiLogbookCardEditor
@state() private _config?: LogbookCardConfig; @state() private _config?: LogbookCardConfig;
@state() private _configEntities?: string[];
public setConfig(config: LogbookCardConfig): void { public setConfig(config: LogbookCardConfig): void {
assert(config, cardConfigStruct); assert(config, cardConfigStruct);
this._config = config; this._config = config;
this._configEntities = config.entities;
}
get _title(): string {
return this._config!.title || "";
} }
get _entities(): string[] { get _entities(): string[] {
return this._config!.entities || []; return this._config!.entities || [];
} }
get _hours_to_show(): number {
return this._config!.hours_to_show || 24;
}
get _theme(): string {
return this._config!.theme || "";
}
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.hass || !this._config) { if (!this.hass || !this._config) {
return html``; return html``;
} }
return html` return html`
<div class="card-config"> <ha-form
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
<div class="side-by-side">
<hui-theme-select-editor
.hass=${this.hass} .hass=${this.hass}
.value=${this._theme} .data=${this._config}
.configValue=${"theme"} .schema=${SCHEMA}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></hui-theme-select-editor> ></ha-form>
<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}
min="1"
.configValue=${"hours_to_show"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<h3> <h3>
${`${this.hass!.localize( ${`${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.entities" "ui.panel.lovelace.editor.card.generic.entities"
@ -111,48 +80,27 @@ export class HuiLogbookCardEditor
</h3> </h3>
<ha-entities-picker <ha-entities-picker
.hass=${this.hass} .hass=${this.hass}
.value=${this._configEntities} .value=${this._entities}
@value-changed=${this._valueChanged} @value-changed=${this._entitiesChanged}
> >
</ha-entities-picker> </ha-entities-picker>
</div>
`; `;
} }
private _valueChanged(ev: CustomEvent): void { private _entitiesChanged(ev: CustomEvent): void {
if (!this._config || !this.hass) { this._config = { ...this._config!, entities: ev.detail.value };
return;
}
const target = ev.target! as EditorTarget;
if (this[`_${target.configValue}`] === target.value) {
return;
}
if (ev.detail && ev.detail.value && Array.isArray(ev.detail.value)) {
this._config = { ...this._config, entities: ev.detail.value };
} else if (target.configValue) {
if (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 }); fireEvent(this, "config-changed", { config: this._config });
} }
static get styles(): CSSResultGroup { private _valueChanged(ev: CustomEvent): void {
return configElementStyle; fireEvent(this, "config-changed", { config: ev.detail.value });
} }
private _computeLabelCallback = (schema: HaFormSchema) =>
this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
) ||
this.hass!.localize(`ui.panel.lovelace.editor.card.logbook.${schema.name}`);
} }
declare global { declare global {

View File

@ -1,3 +1,4 @@
import "../../../../components/ha-form/ha-form";
import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-input";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
@ -12,7 +13,6 @@ import {
assign, assign,
} from "superstruct"; } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import "../../../../components/ha-formfield"; import "../../../../components/ha-formfield";
import "../../../../components/ha-switch"; import "../../../../components/ha-switch";
import { PolymerChangedEvent } from "../../../../polymer-types"; import { PolymerChangedEvent } from "../../../../polymer-types";
@ -27,6 +27,7 @@ import { entitiesConfigStruct } from "../structs/entities-struct";
import { EditorTarget, EntitiesEditorEvent } from "../types"; import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style"; import { configElementStyle } from "./config-elements-style";
import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { HaFormSchema } from "../../../../components/ha-form/types";
const cardConfigStruct = assign( const cardConfigStruct = assign(
baseLovelaceCardConfig, baseLovelaceCardConfig,
@ -41,6 +42,20 @@ const cardConfigStruct = assign(
}) })
); );
const SCHEMA: HaFormSchema[] = [
{ name: "title", selector: { text: {} } },
{
name: "",
type: "grid",
schema: [
{ name: "aspect_ratio", selector: { text: {} } },
{ name: "default_zoom", selector: { number: { mode: "box", min: 0 } } },
{ name: "dark_mode", selector: { boolean: {} } },
{ name: "hours_to_show", selector: { number: { mode: "box", min: 1 } } },
],
},
];
@customElement("hui-map-card-editor") @customElement("hui-map-card-editor")
export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor { export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant; @property({ attribute: false }) public hass?: HomeAssistant;
@ -57,99 +72,24 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
: []; : [];
} }
get _title(): string {
return this._config!.title || "";
}
get _aspect_ratio(): string {
return this._config!.aspect_ratio || "";
}
get _default_zoom(): number {
return this._config!.default_zoom || 0;
}
get _geo_location_sources(): string[] { get _geo_location_sources(): string[] {
return this._config!.geo_location_sources || []; return this._config!.geo_location_sources || [];
} }
get _hours_to_show(): number {
return this._config!.hours_to_show || 0;
}
get _dark_mode(): boolean {
return this._config!.dark_mode || false;
}
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.hass || !this._config) { if (!this.hass || !this._config) {
return html``; return html``;
} }
return html` return html`
<ha-form
.hass=${this.hass}
.data=${this._config}
.schema=${SCHEMA}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
></ha-form>
<div class="card-config"> <div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)}
(${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
<div class="side-by-side">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
)}
(${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._aspect_ratio}
.configValue=${"aspect_ratio"}
@value-changed=${this._valueChanged}
></paper-input>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.map.default_zoom"
)}
(${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
type="number"
.value=${this._default_zoom}
.configValue=${"default_zoom"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<div class="side-by-side">
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.map.dark_mode"
)}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.checked=${this._dark_mode}
.configValue=${"dark_mode"}
@change=${this._valueChanged}
></ha-switch
></ha-formfield>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.map.hours_to_show"
)}
(${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
type="number"
.value=${this._hours_to_show}
.configValue=${"hours_to_show"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<hui-entity-editor <hui-entity-editor
.hass=${this.hass} .hass=${this.hass}
.entities=${this._configEntities} .entities=${this._configEntities}
@ -167,8 +107,7 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
)} )}
.hass=${this.hass} .hass=${this.hass}
.value=${this._geo_location_sources} .value=${this._geo_location_sources}
.configValue=${"geo_location_sources"} @value-changed=${this._geoSourcesChanged}
@value-changed=${this._valueChanged}
></hui-input-list-editor> ></hui-input-list-editor>
</div> </div>
</div> </div>
@ -176,18 +115,15 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
} }
private _entitiesValueChanged(ev: EntitiesEditorEvent): void { private _entitiesValueChanged(ev: EntitiesEditorEvent): void {
if (!this._config || !this.hass) {
return;
}
if (ev.detail && ev.detail.entities) { if (ev.detail && ev.detail.entities) {
this._config = { ...this._config, entities: ev.detail.entities }; this._config = { ...this._config!, entities: ev.detail.entities };
this._configEntities = processEditorEntities(this._config.entities); this._configEntities = processEditorEntities(this._config.entities);
fireEvent(this, "config-changed", { config: this._config }); fireEvent(this, "config-changed", { config: this._config! });
} }
} }
private _valueChanged(ev: PolymerChangedEvent<any>): void { private _geoSourcesChanged(ev: PolymerChangedEvent<any>): void {
if (!this._config || !this.hass) { if (!this._config || !this.hass) {
return; return;
} }
@ -196,26 +132,34 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
return; return;
} }
let value = target.checked ?? ev.detail.value; const value = ev.detail.value;
if (value && target.type === "number") { if (this._geo_location_sources === value) {
value = Number(value);
}
if (this[`_${target.configValue}`] === value) {
return; return;
} }
if (value === "") { if (value === "") {
this._config = { ...this._config }; this._config = { ...this._config };
delete this._config[target.configValue!]; delete this._config.geo_location_sources;
} else if (target.configValue) { } else {
this._config = { this._config = {
...this._config, ...this._config,
[target.configValue]: value, geo_location_sources: value,
}; };
} }
fireEvent(this, "config-changed", { config: this._config }); fireEvent(this, "config-changed", { config: this._config });
} }
private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
}
private _computeLabelCallback = (schema: HaFormSchema) =>
this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
) ||
this.hass!.localize(`ui.panel.lovelace.editor.card.map.${schema.name}`);
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return [ return [
configElementStyle, configElementStyle,

View File

@ -1,16 +1,13 @@
import "@polymer/paper-input/paper-input"; import "../../../../components/ha-form/ha-form";
import "@polymer/paper-input/paper-textarea"; import { html, LitElement, TemplateResult } from "lit";
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { assert, assign, object, optional, string } from "superstruct"; import { assert, assign, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { HomeAssistant } from "../../../../types"; import type { HaFormSchema } from "../../../../components/ha-form/types";
import { MarkdownCardConfig } from "../../cards/types"; import type { HomeAssistant } from "../../../../types";
import "../../components/hui-theme-select-editor"; import type { MarkdownCardConfig } from "../../cards/types";
import { LovelaceCardEditor } from "../../types"; import type { LovelaceCardEditor } from "../../types";
import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
const cardConfigStruct = assign( const cardConfigStruct = assign(
baseLovelaceCardConfig, baseLovelaceCardConfig,
@ -21,6 +18,12 @@ const cardConfigStruct = assign(
}) })
); );
const SCHEMA: HaFormSchema[] = [
{ name: "title", selector: { text: {} } },
{ name: "content", required: true, selector: { text: { multiline: true } } },
{ name: "theme", selector: { theme: {} } },
];
@customElement("hui-markdown-card-editor") @customElement("hui-markdown-card-editor")
export class HuiMarkdownCardEditor export class HuiMarkdownCardEditor
extends LitElement extends LitElement
@ -35,90 +38,33 @@ export class HuiMarkdownCardEditor
this._config = config; this._config = config;
} }
get _title(): string {
return this._config!.title || "";
}
get _content(): string {
return this._config!.content || "";
}
get _theme(): string {
return this._config!.theme || "";
}
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.hass || !this._config) { if (!this.hass || !this._config) {
return html``; return html``;
} }
return html` return html`
<div class="card-config"> <ha-form
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
<paper-textarea
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.markdown.content"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.value=${this._content}
.configValue=${"content"}
@keydown=${this._ignoreKeydown}
@value-changed=${this._valueChanged}
autocapitalize="none"
autocomplete="off"
spellcheck="false"
></paper-textarea>
<hui-theme-select-editor
.hass=${this.hass} .hass=${this.hass}
.value=${this._theme} .data=${this._config}
.configValue=${"theme"} .schema=${SCHEMA}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></hui-theme-select-editor> ></ha-form>
</div>
`; `;
} }
private _ignoreKeydown(ev: KeyboardEvent) { private _valueChanged(ev: CustomEvent): void {
// Stop keyboard events from the paper-textarea from propagating to avoid accidentally closing the dialog when the user presses Enter. fireEvent(this, "config-changed", { config: ev.detail.value });
ev.stopPropagation();
} }
private _valueChanged(ev: EntitiesEditorEvent): void { private _computeLabelCallback = (schema: HaFormSchema) =>
if (!this._config || !this.hass) { this.hass!.localize(
return; `ui.panel.lovelace.editor.card.generic.${schema.name}`
} ) ||
const target = ev.target! as EditorTarget; this.hass!.localize(
`ui.panel.lovelace.editor.card.markdown.${schema.name}`
if (this[`_${target.configValue}`] === target.value) { );
return;
}
if (target.configValue) {
if (target.value === "" && target.configValue !== "content") {
this._config = { ...this._config };
delete this._config[target.configValue!];
} else {
this._config = {
...this._config,
[target.configValue!]: target.value,
};
}
}
fireEvent(this, "config-changed", { config: this._config });
}
static get styles(): CSSResultGroup {
return configElementStyle;
}
} }
declare global { declare global {

View File

@ -1,5 +1,5 @@
import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-input";
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { assert, object, optional, string, assign } from "superstruct"; import { assert, object, optional, string, assign } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
@ -63,7 +63,7 @@ export class HuiPictureCardEditor
return html` return html`
<div class="card-config"> <div class="card-config">
<paper-input <ha-textfield
.label="${this.hass.localize( .label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.image" "ui.panel.lovelace.editor.card.generic.image"
)} (${this.hass.localize( )} (${this.hass.localize(
@ -71,8 +71,8 @@ export class HuiPictureCardEditor
)})" )})"
.value=${this._image} .value=${this._image}
.configValue=${"image"} .configValue=${"image"}
@value-changed=${this._valueChanged} @input=${this._valueChanged}
></paper-input> ></ha-textfield>
<hui-theme-select-editor <hui-theme-select-editor
.hass=${this.hass} .hass=${this.hass}
.value=${this._theme} .value=${this._theme}
@ -134,7 +134,15 @@ export class HuiPictureCardEditor
} }
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return configElementStyle; return [
configElementStyle,
css`
ha-textfield {
display: block;
margin-bottom: 8px;
}
`,
];
} }
} }

View File

@ -1,24 +1,16 @@
import "@material/mwc-list/mwc-list-item";
import "@polymer/paper-input/paper-input";
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { assert, assign, boolean, object, optional, string } from "superstruct"; import { assert, assign, boolean, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { stopPropagation } from "../../../../common/dom/stop_propagation"; import type { HaFormSchema } from "../../../../components/ha-form/types";
import { computeRTLDirection } from "../../../../common/util/compute_rtl"; import type { ActionConfig } from "../../../../data/lovelace";
import "../../../../components/ha-formfield"; import type { HomeAssistant } from "../../../../types";
import "../../../../components/ha-select"; import type { PictureEntityCardConfig } from "../../cards/types";
import "../../../../components/ha-switch";
import { ActionConfig } from "../../../../data/lovelace";
import { HomeAssistant } from "../../../../types";
import { PictureEntityCardConfig } from "../../cards/types";
import "../../components/hui-action-editor"; import "../../components/hui-action-editor";
import "../../components/hui-entity-editor"; import type { LovelaceCardEditor } from "../../types";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import { actionConfigStruct } from "../structs/action-struct"; import { actionConfigStruct } from "../structs/action-struct";
import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { EditorTarget } from "../types"; import type { EditorTarget } from "../types";
import { configElementStyle } from "./config-elements-style"; import { configElementStyle } from "./config-elements-style";
const cardConfigStruct = assign( const cardConfigStruct = assign(
@ -38,7 +30,38 @@ const cardConfigStruct = assign(
}) })
); );
const includeDomains = ["camera"]; const SCHEMA: HaFormSchema[] = [
{ name: "entity", required: true, selector: { entity: {} } },
{ name: "name", selector: { text: {} } },
{ name: "image", selector: { text: {} } },
{ name: "camera_image", selector: { entity: { domain: "camera" } } },
{
name: "",
type: "grid",
schema: [
{
name: "camera_view",
selector: { select: { options: ["auto", "live"] } },
},
{ name: "aspect_ratio", selector: { text: {} } },
],
},
{
name: "",
type: "grid",
schema: [
{
name: "show_name",
selector: { boolean: {} },
},
{
name: "show_state",
selector: { boolean: {} },
},
],
},
{ name: "theme", selector: { theme: {} } },
];
@customElement("hui-picture-entity-card-editor") @customElement("hui-picture-entity-card-editor")
export class HuiPictureEntityCardEditor export class HuiPictureEntityCardEditor
@ -54,30 +77,6 @@ export class HuiPictureEntityCardEditor
this._config = config; this._config = config;
} }
get _entity(): string {
return this._config!.entity || "";
}
get _name(): string {
return this._config!.name || "";
}
get _image(): string {
return this._config!.image || "";
}
get _camera_image(): string {
return this._config!.camera_image || "";
}
get _camera_view(): string {
return this._config!.camera_view || "auto";
}
get _aspect_ratio(): string {
return this._config!.aspect_ratio || "";
}
get _tap_action(): ActionConfig { get _tap_action(): ActionConfig {
return this._config!.tap_action || { action: "more-info" }; return this._config!.tap_action || { action: "more-info" };
} }
@ -86,140 +85,29 @@ export class HuiPictureEntityCardEditor
return this._config!.hold_action; return this._config!.hold_action;
} }
get _show_name(): boolean {
return this._config!.show_name ?? true;
}
get _show_state(): boolean {
return this._config!.show_state ?? true;
}
get _theme(): string {
return this._config!.theme || "";
}
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.hass || !this._config) { if (!this.hass || !this._config) {
return html``; return html``;
} }
const actions = ["more-info", "toggle", "navigate", "call-service", "none"]; const actions = ["more-info", "toggle", "navigate", "call-service", "none"];
const views = ["auto", "live"];
const dir = computeRTLDirection(this.hass!); const data = {
show_state: true,
show_name: true,
camera_view: "auto",
...this._config,
};
return html` return html`
<ha-form
.hass=${this.hass}
.data=${data}
.schema=${SCHEMA}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
></ha-form>
<div class="card-config"> <div class="card-config">
<ha-entity-picker
.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"}
@value-changed=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged}
></paper-input>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.image"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._image}
.configValue=${"image"}
@value-changed=${this._valueChanged}
></paper-input>
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.camera_image"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.hass=${this.hass}
.value=${this._camera_image}
.configValue=${"camera_image"}
@value-changed=${this._valueChanged}
.includeDomains=${includeDomains}
allow-custom-entity
></ha-entity-picker>
<div class="side-by-side">
<ha-select
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.camera_view"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.configValue=${"camera_view"}
@selected=${this._valueChanged}
@closed=${stopPropagation}
fixedMenuPosition
naturalMenuWidth
.value=${views.indexOf(this._camera_view)}
>
${views.map(
(view) =>
html`<mwc-list-item .value=${view}>${view}</mwc-list-item> `
)}
</ha-select>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._aspect_ratio}
.configValue=${"aspect_ratio"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<div class="side-by-side">
<div>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_name"
)}
.dir=${dir}
>
<ha-switch
.checked=${this._config!.show_name !== false}
.configValue=${"show_name"}
@change=${this._change}
></ha-switch
></ha-formfield>
</div>
<div>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_state"
)}
.dir=${dir}
>
<ha-switch
.checked=${this._config!.show_state !== false}
.configValue=${"show_state"}
@change=${this._change}
></ha-switch
></ha-formfield>
</div>
</div>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
<div class="side-by-side"> <div class="side-by-side">
<hui-action-editor <hui-action-editor
.label="${this.hass.localize( .label="${this.hass.localize(
@ -231,7 +119,7 @@ export class HuiPictureEntityCardEditor
.config=${this._tap_action} .config=${this._tap_action}
.actions=${actions} .actions=${actions}
.configValue=${"tap_action"} .configValue=${"tap_action"}
@value-changed=${this._valueChanged} @value-changed=${this._changed}
></hui-action-editor> ></hui-action-editor>
<hui-action-editor <hui-action-editor
.label="${this.hass.localize( .label="${this.hass.localize(
@ -243,32 +131,18 @@ export class HuiPictureEntityCardEditor
.config=${this._hold_action} .config=${this._hold_action}
.actions=${actions} .actions=${actions}
.configValue=${"hold_action"} .configValue=${"hold_action"}
@value-changed=${this._valueChanged} @value-changed=${this._changed}
></hui-action-editor> ></hui-action-editor>
</div> </div>
</div> </div>
`; `;
} }
private _change(ev: Event) {
if (!this._config || !this.hass) {
return;
}
const target = ev.target! as EditorTarget;
const value = target.checked;
if (this[`_${target.configValue}`] === value) {
return;
}
this._config = {
...this._config,
[target.configValue!]: value,
};
fireEvent(this, "config-changed", { config: this._config });
}
private _valueChanged(ev: CustomEvent): void { private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
}
private _changed(ev: CustomEvent): void {
if (!this._config || !this.hass) { if (!this._config || !this.hass) {
return; return;
} }
@ -279,7 +153,6 @@ export class HuiPictureEntityCardEditor
return; return;
} }
if (target.configValue) {
if (value !== false && !value) { if (value !== false && !value) {
this._config = { ...this._config }; this._config = { ...this._config };
delete this._config[target.configValue!]; delete this._config[target.configValue!];
@ -289,13 +162,27 @@ export class HuiPictureEntityCardEditor
[target.configValue!]: value, [target.configValue!]: value,
}; };
} }
}
fireEvent(this, "config-changed", { config: this._config }); fireEvent(this, "config-changed", { config: this._config });
} }
static get styles(): CSSResultGroup { private _computeLabelCallback = (schema: HaFormSchema) => {
return configElementStyle; if (schema.name === "entity") {
return this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.entity"
);
} }
return (
this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
) ||
this.hass!.localize(
`ui.panel.lovelace.editor.card.picture-entity.${schema.name}`
)
);
};
static styles: CSSResultGroup = configElementStyle;
} }
declare global { declare global {

View File

@ -1,16 +1,13 @@
import "@polymer/paper-input/paper-input"; import "../../../../components/ha-form/ha-form";
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { assert, assign, object, optional, string } from "superstruct"; import { assert, assign, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/entity/ha-entity-picker"; import type { HaFormSchema } from "../../../../components/ha-form/types";
import { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
import { PlantStatusCardConfig } from "../../cards/types"; import type { PlantStatusCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor"; import type { LovelaceCardEditor } from "../../types";
import { LovelaceCardEditor } from "../../types";
import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
const cardConfigStruct = assign( const cardConfigStruct = assign(
baseLovelaceCardConfig, baseLovelaceCardConfig,
@ -21,7 +18,11 @@ const cardConfigStruct = assign(
}) })
); );
const includeDomains = ["plant"]; const SCHEMA: HaFormSchema[] = [
{ name: "entity", required: true, selector: { entity: { domain: "plant" } } },
{ name: "name", selector: { text: {} } },
{ name: "theme", selector: { theme: {} } },
];
@customElement("hui-plant-status-card-editor") @customElement("hui-plant-status-card-editor")
export class HuiPlantStatusCardEditor export class HuiPlantStatusCardEditor
@ -37,83 +38,37 @@ export class HuiPlantStatusCardEditor
this._config = config; this._config = config;
} }
get _entity(): string {
return this._config!.entity || "";
}
get _name(): string {
return this._config!.name || "";
}
get _theme(): string {
return this._config!.theme || "";
}
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.hass || !this._config) { if (!this.hass || !this._config) {
return html``; return html``;
} }
return html` return html`
<div class="card-config"> <ha-form
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass} .hass=${this.hass}
.value=${this._entity} .data=${this._config}
.configValue=${"entity"} .schema=${SCHEMA}
.includeDomains=${includeDomains} .computeLabel=${this._computeLabelCallback}
@change=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></paper-input> ></ha-form>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
`; `;
} }
private _valueChanged(ev: EntitiesEditorEvent): void { private _valueChanged(ev: CustomEvent): void {
if (!this._config || !this.hass) { fireEvent(this, "config-changed", { config: ev.detail.value });
return;
}
const target = ev.target! as EditorTarget;
if (this[`_${target.configValue}`] === target.value) {
return;
}
if (target.configValue) {
if (target.value === "") {
this._config = { ...this._config };
delete this._config[target.configValue!];
} else {
this._config = {
...this._config,
[target.configValue!]: target.value,
};
}
}
fireEvent(this, "config-changed", { config: this._config });
} }
static get styles(): CSSResultGroup { private _computeLabelCallback = (schema: HaFormSchema) => {
return configElementStyle; if (schema.name === "entity") {
return this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.entity"
);
} }
return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
);
};
} }
declare global { declare global {

View File

@ -148,11 +148,9 @@ export class HuiSensorCardEditor
private _computeLabelCallback = (schema: HaFormSchema) => { private _computeLabelCallback = (schema: HaFormSchema) => {
if (schema.name === "entity") { if (schema.name === "entity") {
return `${this.hass!.localize( return this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.entity" "ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass!.localize( );
"ui.panel.lovelace.editor.card.config.required"
)})`;
} }
if (schema.name === "detail") { if (schema.name === "detail") {

View File

@ -66,11 +66,9 @@ export class HuiThermostatCardEditor
private _computeLabelCallback = (schema: HaFormSchema) => { private _computeLabelCallback = (schema: HaFormSchema) => {
if (schema.name === "entity") { if (schema.name === "entity") {
return `${this.hass!.localize( return this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.entity" "ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass!.localize( );
"ui.panel.lovelace.editor.card.config.required"
)})`;
} }
return this.hass!.localize( return this.hass!.localize(

View File

@ -16,7 +16,7 @@ export class HuiDialogEditLovelace extends LitElement {
@state() private _lovelace?: Lovelace; @state() private _lovelace?: Lovelace;
private _config?: LovelaceConfig; @state() private _config?: LovelaceConfig;
private _saving = false; private _saving = false;

View File

@ -1,10 +1,9 @@
import "@polymer/paper-input/paper-input"; import "../../../../components/ha-textfield";
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { LovelaceConfig } from "../../../../data/lovelace"; import { LovelaceConfig } from "../../../../data/lovelace";
import { HomeAssistant } from "../../../../types"; import { HomeAssistant } from "../../../../types";
import { configElementStyle } from "../config-elements/config-elements-style";
import { EditorTarget } from "../types"; import { EditorTarget } from "../types";
declare global { declare global {
@ -30,16 +29,14 @@ export class HuiLovelaceEditor extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<div class="card-config"> <ha-textfield
<paper-input
.label=${this.hass.localize( .label=${this.hass.localize(
"ui.panel.lovelace.editor.edit_lovelace.title" "ui.panel.lovelace.editor.edit_lovelace.title"
)} )}
.value=${this._title} .value=${this._title}
.configValue=${"title"} .configValue=${"title"}
@value-changed=${this._valueChanged} @change=${this._valueChanged}
></paper-input> ></ha-textfield>
</div>
`; `;
} }
@ -66,9 +63,11 @@ export class HuiLovelaceEditor extends LitElement {
fireEvent(this, "lovelace-config-changed", { config: newConfig }); fireEvent(this, "lovelace-config-changed", { config: newConfig });
} }
static get styles(): CSSResultGroup { static styles: CSSResultGroup = css`
return configElementStyle; ha-textfield {
display: block;
} }
`;
} }
declare global { declare global {

View File

@ -1,24 +1,17 @@
import "@material/mwc-list/mwc-list-item"; import "../../../../components/ha-form/ha-form";
import "@polymer/paper-input/paper-input"; import { html, LitElement, TemplateResult } from "lit";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { stopPropagation } from "../../../../common/dom/stop_propagation";
import { slugify } from "../../../../common/string/slugify"; import { slugify } from "../../../../common/string/slugify";
import "../../../../components/ha-formfield"; import type { HaFormSchema } from "../../../../components/ha-form/types";
import "../../../../components/ha-icon-picker"; import type { LovelaceViewConfig } from "../../../../data/lovelace";
import "../../../../components/ha-select"; import type { HomeAssistant } from "../../../../types";
import "../../../../components/ha-switch";
import { LovelaceViewConfig } from "../../../../data/lovelace";
import { HomeAssistant } from "../../../../types";
import "../../components/hui-theme-select-editor";
import { import {
DEFAULT_VIEW_LAYOUT, DEFAULT_VIEW_LAYOUT,
PANEL_VIEW_LAYOUT, PANEL_VIEW_LAYOUT,
SIDEBAR_VIEW_LAYOUT, SIDEBAR_VIEW_LAYOUT,
} from "../../views/const"; } from "../../views/const";
import { configElementStyle } from "../config-elements/config-elements-style";
import { EditorTarget } from "../types";
declare global { declare global {
interface HASSDomEvents { interface HASSDomEvents {
@ -38,32 +31,35 @@ export class HuiViewEditor extends LitElement {
private _suggestedPath = false; private _suggestedPath = false;
get _path(): string { private _schema = memoizeOne((localize): HaFormSchema[] => [
if (!this._config) { { name: "title", selector: { text: {} } },
return ""; {
} name: "icon",
return this._config.path || ""; selector: {
} icon: {},
},
},
{ name: "path", selector: { text: {} } },
{ name: "theme", selector: { theme: {} } },
{
name: "type",
selector: {
select: {
options: [
DEFAULT_VIEW_LAYOUT,
SIDEBAR_VIEW_LAYOUT,
PANEL_VIEW_LAYOUT,
].map((type) => ({
value: type,
label: localize(`ui.panel.lovelace.editor.edit_view.types.${type}`),
})),
},
},
},
]);
get _title(): string { set config(config: LovelaceViewConfig) {
if (!this._config) { this._config = config;
return "";
}
return this._config.title || "";
}
get _icon(): string {
if (!this._config) {
return "";
}
return this._config.icon || "";
}
get _theme(): string {
if (!this._config) {
return "";
}
return this._config.theme || "Backend-selected";
} }
get _type(): string { get _type(): string {
@ -75,142 +71,59 @@ export class HuiViewEditor extends LitElement {
: this._config.type || DEFAULT_VIEW_LAYOUT; : this._config.type || DEFAULT_VIEW_LAYOUT;
} }
set config(config: LovelaceViewConfig) {
this._config = config;
}
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.hass) { if (!this.hass) {
return html``; return html``;
} }
const schema = this._schema(this.hass.localize);
const data = {
theme: "Backend-selected",
...this._config,
type: this._type,
};
return html` return html`
<div class="card-config"> <ha-form
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
@blur=${this._handleTitleBlur}
></paper-input>
<ha-icon-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.icon"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._icon}
.placeholder=${this._icon}
.configValue=${"icon"}
@value-changed=${this._valueChanged}
></ha-icon-picker>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.url"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._path}
.configValue=${"path"}
@value-changed=${this._valueChanged}
></paper-input>
<hui-theme-select-editor
.hass=${this.hass} .hass=${this.hass}
.value=${this._theme} .data=${data}
.configValue=${"theme"} .schema=${schema}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></hui-theme-select-editor> ></ha-form>
<ha-select
.label=${this.hass.localize(
"ui.panel.lovelace.editor.edit_view.type"
)}
.value=${this._type}
@selected=${this._typeChanged}
@closed=${stopPropagation}
fixedMenuPosition
naturalMenuWidth
>
${[DEFAULT_VIEW_LAYOUT, SIDEBAR_VIEW_LAYOUT, PANEL_VIEW_LAYOUT].map(
(type) => html`<mwc-list-item .value=${type}>
${this.hass.localize(
`ui.panel.lovelace.editor.edit_view.types.${type}`
)}
</mwc-list-item>`
)}
</ha-select>
</div>
`; `;
} }
private _valueChanged(ev: Event): void { private _valueChanged(ev: CustomEvent): void {
const target = ev.currentTarget! as EditorTarget; const config = ev.detail.value;
if (this[`_${target.configValue}`] === target.value) { if (config.type === "masonry") {
return; delete config.type;
} }
let newConfig;
if (target.configValue) {
newConfig = {
...this._config,
[target.configValue!]:
target.checked !== undefined ? target.checked : target.value,
};
}
fireEvent(this, "view-config-changed", { config: newConfig });
}
private _typeChanged(ev): void {
const selected = ev.target.value;
if (selected === "") {
return;
}
const newConfig = {
...this._config,
};
delete newConfig.panel;
if (selected === "masonry") {
delete newConfig.type;
} else {
newConfig.type = selected;
}
fireEvent(this, "view-config-changed", { config: newConfig });
}
private _handleTitleBlur(ev) {
if ( if (
!this.isNew || this.isNew &&
this._suggestedPath || !this._suggestedPath &&
this._config.path || config.title &&
!ev.currentTarget.value (!this._config.path ||
config.path === slugify(this._config.title || "", "-"))
) { ) {
return; config.path = slugify(config.title, "-");
} }
const config = {
...this._config,
path: slugify(ev.currentTarget.value, "-"),
};
fireEvent(this, "view-config-changed", { config }); fireEvent(this, "view-config-changed", { config });
} }
static get styles(): CSSResultGroup { private _computeLabelCallback = (schema: HaFormSchema) => {
return [ if (schema.name === "path") {
configElementStyle, return this.hass!.localize(`ui.panel.lovelace.editor.card.generic.url`);
css`
.panel {
color: var(--secondary-text-color);
display: block;
}
`,
];
} }
return (
this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
) || this.hass.localize("ui.panel.lovelace.editor.edit_view.type")
);
};
} }
declare global { declare global {

View File

@ -3520,6 +3520,9 @@
"name": "Entity Filter", "name": "Entity Filter",
"description": "The Entity Filter card allows you to define a list of entities that you want to track only when in a certain state." "description": "The Entity Filter card allows you to define a list of entities that you want to track only when in a certain state."
}, },
"entity-row": {
"secondary_info": "Secondary Information"
},
"gauge": { "gauge": {
"name": "Gauge", "name": "Gauge",
"needle_gauge": "Display as needle gauge?", "needle_gauge": "Display as needle gauge?",