Add conditional form schema

This commit is contained in:
Paul Bottein 2023-06-08 16:38:07 +02:00
parent 8580d3f9bf
commit 261cc6598d
No known key found for this signature in database
4 changed files with 168 additions and 82 deletions

View File

@ -0,0 +1,74 @@
import {
css,
CSSResultGroup,
html,
LitElement,
nothing,
PropertyValues,
} from "lit";
import { customElement, property } from "lit/decorators";
import type { HomeAssistant } from "../../types";
import "./ha-form";
import type {
HaFormDataContainer,
HaFormElement,
HaFormConditionalSchema,
HaFormSchema,
} from "./types";
@customElement("ha-form-conditional")
export class HaFormConditional extends LitElement implements HaFormElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public data!: HaFormDataContainer;
@property({ attribute: false }) public schema!: HaFormConditionalSchema;
@property({ type: Boolean }) public disabled = false;
@property() public computeLabel?: (
schema: HaFormSchema,
data?: HaFormDataContainer
) => string;
@property() public computeHelper?: (schema: HaFormSchema) => string;
protected updated(changedProps: PropertyValues): void {
if (changedProps.has("schema") || changedProps.has("data")) {
this.toggleAttribute("hidden", !this.schema.condition(this.data));
}
}
protected render() {
if (!this.schema.condition(this.data)) {
return nothing;
}
return html`
<ha-form
.hass=${this.hass}
.data=${this.data}
.schema=${this.schema.schema}
.disabled=${this.disabled}
.computeLabel=${this.computeLabel}
.computeHelper=${this.computeHelper}
></ha-form>
`;
}
static get styles(): CSSResultGroup {
return css`
:host([hidden]) {
display: none !important;
}
:host ha-form {
display: block;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-form-conditional": HaFormConditional;
}
}

View File

@ -21,6 +21,7 @@ const LOAD_ELEMENTS = {
float: () => import("./ha-form-float"),
grid: () => import("./ha-form-grid"),
expandable: () => import("./ha-form-expandable"),
conditional: () => import("./ha-form-conditional"),
integer: () => import("./ha-form-integer"),
multi_select: () => import("./ha-form-multi_select"),
positive_time_period_dict: () =>
@ -189,12 +190,13 @@ export class HaForm extends LitElement implements HaFormElement {
static get styles(): CSSResultGroup {
return css`
.root {
display: grid;
row-gap: 24px;
}
.root > * {
display: block;
}
.root > *:not([own-margin]):not(:last-child) {
margin-bottom: 24px;
}
ha-alert[own-margin] {
margin-bottom: 4px;
}

View File

@ -13,7 +13,8 @@ export type HaFormSchema =
| HaFormTimeSchema
| HaFormSelector
| HaFormGridSchema
| HaFormExpandableSchema;
| HaFormExpandableSchema
| HaFormConditionalSchema;
export interface HaFormBaseSchema {
name: string;
@ -47,6 +48,13 @@ export interface HaFormExpandableSchema extends HaFormBaseSchema {
schema: readonly HaFormSchema[];
}
export interface HaFormConditionalSchema extends HaFormBaseSchema {
type: "conditional";
name: "";
condition: (data: HaFormDataContainer) => boolean;
schema: readonly HaFormSchema[];
}
export interface HaFormSelector extends HaFormBaseSchema {
type?: never;
selector: Selector;
@ -99,7 +107,10 @@ export interface HaFormTimeSchema extends HaFormBaseSchema {
export type SchemaUnion<
SchemaArray extends readonly HaFormSchema[],
Schema = SchemaArray[number]
> = Schema extends HaFormGridSchema | HaFormExpandableSchema
> = Schema extends
| HaFormGridSchema
| HaFormExpandableSchema
| HaFormConditionalSchema
? SchemaUnion<Schema["schema"]>
: Schema;

View File

@ -1,6 +1,5 @@
import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import {
array,
assert,
@ -13,7 +12,10 @@ import {
} from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-form/ha-form";
import type { SchemaUnion } from "../../../../components/ha-form/types";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types";
import type { GaugeCardConfig } from "../../cards/types";
import type { LovelaceCardEditor } from "../../types";
@ -41,6 +43,75 @@ const cardConfigStruct = assign(
})
);
const SCHEMA = [
{
name: "entity",
selector: {
entity: {
domain: ["counter", "input_number", "number", "sensor"],
},
},
},
{
name: "",
type: "grid",
schema: [
{ name: "name", selector: { text: {} } },
{ name: "unit", selector: { text: {} } },
],
},
{ name: "theme", selector: { theme: {} } },
{
name: "",
type: "grid",
schema: [
{
name: "min",
default: DEFAULT_MIN,
selector: { number: { mode: "box" } },
},
{
name: "max",
default: DEFAULT_MAX,
selector: { number: { mode: "box" } },
},
],
},
{
name: "",
type: "grid",
schema: [
{ name: "needle", selector: { boolean: {} } },
{ name: "show_severity", selector: { boolean: {} } },
],
},
{
name: "",
type: "conditional",
condition: (data) => !!data.show_severity,
schema: [
{
name: "severity",
type: "grid",
schema: [
{
name: "green",
selector: { number: { mode: "box" } },
},
{
name: "yellow",
selector: { number: { mode: "box" } },
},
{
name: "red",
selector: { number: { mode: "box" } },
},
],
},
],
},
] as const satisfies readonly HaFormSchema[];
@customElement("hui-gauge-card-editor")
export class HuiGaugeCardEditor
extends LitElement
@ -55,81 +126,11 @@ export class HuiGaugeCardEditor
this._config = config;
}
private _schema = memoizeOne(
(showSeverity: boolean) =>
[
{
name: "entity",
selector: {
entity: {
domain: ["counter", "input_number", "number", "sensor"],
},
},
},
{
name: "",
type: "grid",
schema: [
{ name: "name", selector: { text: {} } },
{ name: "unit", selector: { text: {} } },
],
},
{ name: "theme", selector: { theme: {} } },
{
name: "",
type: "grid",
schema: [
{
name: "min",
default: DEFAULT_MIN,
selector: { number: { mode: "box" } },
},
{
name: "max",
default: DEFAULT_MAX,
selector: { number: { mode: "box" } },
},
],
},
{
name: "",
type: "grid",
schema: [
{ name: "needle", selector: { boolean: {} } },
{ name: "show_severity", selector: { boolean: {} } },
],
},
...(showSeverity
? ([
{
name: "severity",
type: "grid",
schema: [
{
name: "green",
selector: { number: { mode: "box" } },
},
{
name: "yellow",
selector: { number: { mode: "box" } },
},
{
name: "red",
selector: { number: { mode: "box" } },
},
],
},
] as const)
: []),
] as const
);
protected render() {
if (!this.hass || !this._config) {
return nothing;
}
const schema = this._schema(this._config!.severity !== undefined);
const data = {
show_severity: this._config!.severity !== undefined,
...this._config,
@ -139,7 +140,7 @@ export class HuiGaugeCardEditor
<ha-form
.hass=${this.hass}
.data=${data}
.schema=${schema}
.schema=${SCHEMA}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
></ha-form>
@ -153,7 +154,7 @@ export class HuiGaugeCardEditor
config = {
...config,
severity: {
green: config.green || config.severity?.green || 0,
green: config.green || config.severity?.green,
yellow: config.yellow || config.severity?.yellow || 0,
red: config.red || config.severity?.red || 0,
},
@ -170,9 +171,7 @@ export class HuiGaugeCardEditor
fireEvent(this, "config-changed", { config });
}
private _computeLabelCallback = (
schema: SchemaUnion<ReturnType<typeof this._schema>>
) => {
private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) => {
switch (schema.name) {
case "name":
return this.hass!.localize(