mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 17:26:42 +00:00
Use MWC components for ha-form (#10120)
This commit is contained in:
parent
fa52442c1c
commit
a839494a1e
@ -1,23 +1,26 @@
|
|||||||
/* eslint-disable lit/no-template-arrow */
|
/* eslint-disable lit/no-template-arrow */
|
||||||
|
import "@material/mwc-button";
|
||||||
import { LitElement, TemplateResult, css, html } from "lit";
|
import { LitElement, TemplateResult, css, html } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
import "../../../src/components/ha-form/ha-form";
|
import { computeInitialHaFormData } from "../../../src/components/ha-form/compute-initial-ha-form-data";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
||||||
import type { HaFormSchema } from "../../../src/components/ha-form/ha-form";
|
import type { HaFormSchema } from "../../../src/components/ha-form/types";
|
||||||
|
import "../../../src/components/ha-form/ha-form";
|
||||||
|
|
||||||
const SCHEMAS: {
|
const SCHEMAS: {
|
||||||
title: string;
|
title: string;
|
||||||
translations?: Record<string, string>;
|
translations?: Record<string, string>;
|
||||||
error?: Record<string, string>;
|
error?: Record<string, string>;
|
||||||
schema: HaFormSchema[];
|
schema: HaFormSchema[];
|
||||||
|
data?: Record<string, any>;
|
||||||
}[] = [
|
}[] = [
|
||||||
{
|
{
|
||||||
title: "Authentication",
|
title: "Authentication",
|
||||||
translations: {
|
translations: {
|
||||||
username: "Username",
|
username: "Username",
|
||||||
password: "Password",
|
password: "Password",
|
||||||
invalid_login: "Invalid login",
|
invalid_login: "Invalid username or password",
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
base: "invalid_login",
|
base: "invalid_login",
|
||||||
@ -57,6 +60,11 @@ const SCHEMAS: {
|
|||||||
optional: true,
|
optional: true,
|
||||||
default: 10,
|
default: 10,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "float",
|
||||||
|
name: "float",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: "string",
|
type: "string",
|
||||||
name: "string",
|
name: "string",
|
||||||
@ -83,6 +91,80 @@ const SCHEMAS: {
|
|||||||
optional: true,
|
optional: true,
|
||||||
default: ["default"],
|
default: ["default"],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "positive_time_period_dict",
|
||||||
|
name: "time",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Numbers",
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
type: "integer",
|
||||||
|
name: "int",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "integer",
|
||||||
|
name: "int with default",
|
||||||
|
optional: true,
|
||||||
|
default: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "integer",
|
||||||
|
name: "int range required",
|
||||||
|
required: true,
|
||||||
|
default: 5,
|
||||||
|
valueMin: 0,
|
||||||
|
valueMax: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "integer",
|
||||||
|
name: "int range optional",
|
||||||
|
optional: true,
|
||||||
|
valueMin: 0,
|
||||||
|
valueMax: 10,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "select",
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
options: [
|
||||||
|
["default", "Default"],
|
||||||
|
["other", "Other"],
|
||||||
|
],
|
||||||
|
name: "select",
|
||||||
|
required: true,
|
||||||
|
default: "default",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
options: [
|
||||||
|
["default", "Default"],
|
||||||
|
["other", "Other"],
|
||||||
|
],
|
||||||
|
name: "select optional",
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
options: [
|
||||||
|
["default", "Default"],
|
||||||
|
["other", "Other"],
|
||||||
|
["uno", "mas"],
|
||||||
|
["one", "more"],
|
||||||
|
["and", "another_one"],
|
||||||
|
["option", "1000"],
|
||||||
|
],
|
||||||
|
name: "select many otions",
|
||||||
|
optional: true,
|
||||||
|
default: "default",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -95,7 +177,7 @@ const SCHEMAS: {
|
|||||||
other: "Other",
|
other: "Other",
|
||||||
},
|
},
|
||||||
name: "multi",
|
name: "multi",
|
||||||
optional: true,
|
required: true,
|
||||||
default: ["default"],
|
default: ["default"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -108,19 +190,46 @@ const SCHEMAS: {
|
|||||||
and: "another_one",
|
and: "another_one",
|
||||||
option: "1000",
|
option: "1000",
|
||||||
},
|
},
|
||||||
name: "multi",
|
name: "multi many otions",
|
||||||
optional: true,
|
optional: true,
|
||||||
default: ["default"],
|
default: ["default"],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Field specific error",
|
||||||
|
data: {
|
||||||
|
new_password: "hello",
|
||||||
|
new_password_2: "bye",
|
||||||
|
},
|
||||||
|
translations: {
|
||||||
|
new_password: "New Password",
|
||||||
|
new_password_2: "Re-type Password",
|
||||||
|
not_match: "The passwords do not match",
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
new_password_2: "not_match",
|
||||||
|
},
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
type: "string",
|
||||||
|
name: "new_password",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "string",
|
||||||
|
name: "new_password_2",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@customElement("demo-ha-form")
|
@customElement("demo-ha-form")
|
||||||
class DemoHaForm extends LitElement {
|
class DemoHaForm extends LitElement {
|
||||||
private lightModeData: any = [];
|
private data = SCHEMAS.map(
|
||||||
|
({ schema, data }) => data || computeInitialHaFormData(schema)
|
||||||
private darkModeData: any = [];
|
);
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
@ -130,38 +239,58 @@ class DemoHaForm extends LitElement {
|
|||||||
translations[schema.name] || schema.name;
|
translations[schema.name] || schema.name;
|
||||||
const computeError = (error) => translations[error] || error;
|
const computeError = (error) => translations[error] || error;
|
||||||
|
|
||||||
return [
|
return html`
|
||||||
[this.lightModeData, "light"],
|
<div class="row">
|
||||||
[this.darkModeData, "dark"],
|
<div class="content light">
|
||||||
].map(
|
|
||||||
([data, type]) => html`
|
|
||||||
<div class="row" data-type=${type}>
|
|
||||||
<ha-card .header=${info.title}>
|
<ha-card .header=${info.title}>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<ha-form
|
<ha-form
|
||||||
.data=${data[idx]}
|
.data=${this.data[idx]}
|
||||||
.schema=${info.schema}
|
.schema=${info.schema}
|
||||||
.error=${info.error}
|
.error=${info.error}
|
||||||
.computeError=${computeError}
|
.computeError=${computeError}
|
||||||
.computeLabel=${computeLabel}
|
.computeLabel=${computeLabel}
|
||||||
@value-changed=${(e) => {
|
@value-changed=${(e) => {
|
||||||
data[idx] = e.detail.value;
|
this.data[idx] = e.detail.value;
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}}
|
}}
|
||||||
></ha-form>
|
></ha-form>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<mwc-button>Submit</mwc-button>
|
||||||
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
<pre>${JSON.stringify(data[idx], undefined, 2)}</pre>
|
|
||||||
</div>
|
</div>
|
||||||
`
|
<div class="content dark">
|
||||||
);
|
<ha-card .header=${info.title}>
|
||||||
|
<div class="card-content">
|
||||||
|
<ha-form
|
||||||
|
.data=${this.data[idx]}
|
||||||
|
.schema=${info.schema}
|
||||||
|
.error=${info.error}
|
||||||
|
.computeError=${computeError}
|
||||||
|
.computeLabel=${computeLabel}
|
||||||
|
@value-changed=${(e) => {
|
||||||
|
this.data[idx] = e.detail.value;
|
||||||
|
this.requestUpdate();
|
||||||
|
}}
|
||||||
|
></ha-form>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<mwc-button>Submit</mwc-button>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
<pre>${JSON.stringify(this.data[idx], undefined, 2)}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
})}
|
})}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated(changedProps) {
|
firstUpdated(changedProps) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
this.shadowRoot!.querySelectorAll("[data-type=dark]").forEach((el) => {
|
this.shadowRoot!.querySelectorAll(".dark").forEach((el) => {
|
||||||
applyThemesOnElement(
|
applyThemesOnElement(
|
||||||
el,
|
el,
|
||||||
{
|
{
|
||||||
@ -178,28 +307,63 @@ class DemoHaForm extends LitElement {
|
|||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
.row {
|
.row {
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 800px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 50px;
|
}
|
||||||
|
.content {
|
||||||
|
padding: 50px 0;
|
||||||
background-color: var(--primary-background-color);
|
background-color: var(--primary-background-color);
|
||||||
}
|
}
|
||||||
|
.light {
|
||||||
|
flex: 1;
|
||||||
|
padding-left: 50px;
|
||||||
|
padding-right: 50px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.light ha-card {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
.dark {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
padding-left: 50px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
ha-card {
|
ha-card {
|
||||||
width: 100%;
|
width: 400px;
|
||||||
max-width: 384px;
|
|
||||||
}
|
}
|
||||||
pre {
|
pre {
|
||||||
width: 400px;
|
width: 300px;
|
||||||
margin: 0 16px;
|
margin: 0 16px 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
@media only screen and (max-width: 800px) {
|
.card-actions {
|
||||||
.row {
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 1500px) {
|
||||||
|
.light {
|
||||||
|
flex: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 1000px) {
|
||||||
|
.light,
|
||||||
|
.dark {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.row,
|
||||||
|
.dark {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
ha-card {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
pre {
|
pre {
|
||||||
margin: 16px 0;
|
margin: 16px auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -19,7 +19,7 @@ import "../../../../src/components/ha-button-menu";
|
|||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-alert";
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-form/ha-form";
|
import "../../../../src/components/ha-form/ha-form";
|
||||||
import type { HaFormSchema } from "../../../../src/components/ha-form/ha-form";
|
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
|
||||||
import "../../../../src/components/ha-formfield";
|
import "../../../../src/components/ha-formfield";
|
||||||
import "../../../../src/components/ha-switch";
|
import "../../../../src/components/ha-switch";
|
||||||
import "../../../../src/components/ha-yaml-editor";
|
import "../../../../src/components/ha-yaml-editor";
|
||||||
|
@ -60,9 +60,12 @@
|
|||||||
"@material/mwc-menu": "0.25.1",
|
"@material/mwc-menu": "0.25.1",
|
||||||
"@material/mwc-radio": "0.25.1",
|
"@material/mwc-radio": "0.25.1",
|
||||||
"@material/mwc-ripple": "0.25.1",
|
"@material/mwc-ripple": "0.25.1",
|
||||||
|
"@material/mwc-select": "^0.25.1",
|
||||||
|
"@material/mwc-slider": "^0.25.1",
|
||||||
"@material/mwc-switch": "0.25.1",
|
"@material/mwc-switch": "0.25.1",
|
||||||
"@material/mwc-tab": "0.25.1",
|
"@material/mwc-tab": "0.25.1",
|
||||||
"@material/mwc-tab-bar": "0.25.1",
|
"@material/mwc-tab-bar": "0.25.1",
|
||||||
|
"@material/mwc-textfield": "^0.25.1",
|
||||||
"@material/top-app-bar": "13.0.0-canary.65125b3a6.0",
|
"@material/top-app-bar": "13.0.0-canary.65125b3a6.0",
|
||||||
"@mdi/js": "6.2.95",
|
"@mdi/js": "6.2.95",
|
||||||
"@mdi/svg": "6.2.95",
|
"@mdi/svg": "6.2.95",
|
||||||
|
@ -11,12 +11,14 @@ import "./ha-password-manager-polyfill";
|
|||||||
import { property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import "../components/ha-form/ha-form";
|
import "../components/ha-form/ha-form";
|
||||||
import "../components/ha-markdown";
|
import "../components/ha-markdown";
|
||||||
|
import "../components/ha-alert";
|
||||||
import { AuthProvider } from "../data/auth";
|
import { AuthProvider } from "../data/auth";
|
||||||
import {
|
import {
|
||||||
DataEntryFlowStep,
|
DataEntryFlowStep,
|
||||||
DataEntryFlowStepForm,
|
DataEntryFlowStepForm,
|
||||||
} from "../data/data_entry_flow";
|
} from "../data/data_entry_flow";
|
||||||
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
|
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
|
||||||
|
import { computeInitialHaFormData } from "../components/ha-form/compute-initial-ha-form-data";
|
||||||
|
|
||||||
type State = "loading" | "error" | "step";
|
type State = "loading" | "error" | "step";
|
||||||
|
|
||||||
@ -31,12 +33,40 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
|
|
||||||
@state() private _state: State = "loading";
|
@state() private _state: State = "loading";
|
||||||
|
|
||||||
@state() private _stepData: any = {};
|
@state() private _stepData?: Record<string, any>;
|
||||||
|
|
||||||
@state() private _step?: DataEntryFlowStep;
|
@state() private _step?: DataEntryFlowStep;
|
||||||
|
|
||||||
@state() private _errorMessage?: string;
|
@state() private _errorMessage?: string;
|
||||||
|
|
||||||
|
willUpdate(changedProps: PropertyValues) {
|
||||||
|
super.willUpdate(changedProps);
|
||||||
|
|
||||||
|
if (!changedProps.has("_step")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._step) {
|
||||||
|
this._stepData = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldStep = changedProps.get("_step") as HaAuthFlow["_step"];
|
||||||
|
|
||||||
|
if (
|
||||||
|
!oldStep ||
|
||||||
|
this._step.flow_id !== oldStep.flow_id ||
|
||||||
|
(this._step.type === "form" &&
|
||||||
|
oldStep.type === "form" &&
|
||||||
|
this._step.step_id !== oldStep.step_id)
|
||||||
|
) {
|
||||||
|
this._stepData =
|
||||||
|
this._step.type === "form"
|
||||||
|
? computeInitialHaFormData(this._step.data_schema)
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<form>${this._renderForm()}</form>
|
<form>${this._renderForm()}</form>
|
||||||
@ -76,6 +106,24 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
if (changedProps.has("authProvider")) {
|
if (changedProps.has("authProvider")) {
|
||||||
this._providerChanged(this.authProvider);
|
this._providerChanged(this.authProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!changedProps.has("_step") || this._step?.type !== "form") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 100ms to give all the form elements time to initialize.
|
||||||
|
setTimeout(() => {
|
||||||
|
const form = this.renderRoot.querySelector("ha-form");
|
||||||
|
if (form) {
|
||||||
|
(form as any).focus();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.renderRoot.querySelector(
|
||||||
|
"ha-password-manager-polyfill"
|
||||||
|
)!.boundingRect = this.getBoundingClientRect();
|
||||||
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderForm(): TemplateResult {
|
private _renderForm(): TemplateResult {
|
||||||
@ -98,16 +146,20 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
`;
|
`;
|
||||||
case "error":
|
case "error":
|
||||||
return html`
|
return html`
|
||||||
<div class="error">
|
<ha-alert alert-type="error">
|
||||||
${this.localize(
|
${this.localize(
|
||||||
"ui.panel.page-authorize.form.error",
|
"ui.panel.page-authorize.form.error",
|
||||||
"error",
|
"error",
|
||||||
this._errorMessage
|
this._errorMessage
|
||||||
)}
|
)}
|
||||||
</div>
|
</ha-alert>
|
||||||
`;
|
`;
|
||||||
case "loading":
|
case "loading":
|
||||||
return html` ${this.localize("ui.panel.page-authorize.form.working")} `;
|
return html`
|
||||||
|
<ha-alert alert-type="info">
|
||||||
|
${this.localize("ui.panel.page-authorize.form.working")}
|
||||||
|
</ha-alert>
|
||||||
|
`;
|
||||||
default:
|
default:
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
@ -189,7 +241,8 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this._updateStep(data);
|
this._step = data;
|
||||||
|
this._state = "step";
|
||||||
} else {
|
} else {
|
||||||
this._state = "error";
|
this._state = "error";
|
||||||
this._errorMessage = data.message;
|
this._errorMessage = data.message;
|
||||||
@ -220,39 +273,6 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
document.location.assign(url);
|
document.location.assign(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _updateStep(step: DataEntryFlowStep) {
|
|
||||||
let stepData: any = null;
|
|
||||||
if (
|
|
||||||
this._step &&
|
|
||||||
(step.flow_id !== this._step.flow_id ||
|
|
||||||
(step.type === "form" &&
|
|
||||||
this._step.type === "form" &&
|
|
||||||
step.step_id !== this._step.step_id))
|
|
||||||
) {
|
|
||||||
stepData = {};
|
|
||||||
}
|
|
||||||
this._step = step;
|
|
||||||
this._state = "step";
|
|
||||||
if (stepData != null) {
|
|
||||||
this._stepData = stepData;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.updateComplete;
|
|
||||||
// 100ms to give all the form elements time to initialize.
|
|
||||||
setTimeout(() => {
|
|
||||||
const form = this.renderRoot.querySelector("ha-form");
|
|
||||||
if (form) {
|
|
||||||
(form as any).focus();
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.renderRoot.querySelector(
|
|
||||||
"ha-password-manager-polyfill"
|
|
||||||
)!.boundingRect = this.getBoundingClientRect();
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _stepDataChanged(ev: CustomEvent) {
|
private _stepDataChanged(ev: CustomEvent) {
|
||||||
this._stepData = ev.detail.value;
|
this._stepData = ev.detail.value;
|
||||||
}
|
}
|
||||||
@ -316,7 +336,8 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
this._redirect(newStep.result);
|
this._redirect(newStep.result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this._updateStep(newStep);
|
this._step = newStep;
|
||||||
|
this._state = "step";
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error("Error submitting step", err);
|
console.error("Error submitting step", err);
|
||||||
@ -337,9 +358,6 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
margin: 24px 0 8px;
|
margin: 24px 0 8px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.error {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,6 +174,10 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
display: block;
|
display: block;
|
||||||
margin-top: 48px;
|
margin-top: 48px;
|
||||||
}
|
}
|
||||||
|
ha-auth-flow {
|
||||||
|
display: block;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
import { html, LitElement, TemplateResult } from "lit";
|
import { 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 { HaFormSchema } from "../components/ha-form/ha-form";
|
import type { HaFormSchema } from "../components/ha-form/types";
|
||||||
import { DataEntryFlowStep } from "../data/data_entry_flow";
|
import type { DataEntryFlowStep } from "../data/data_entry_flow";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
|
@ -16,8 +16,6 @@ class HaDurationInput extends LitElement {
|
|||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
|
|
||||||
@property() public suffix?: string;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public required?: boolean;
|
@property({ type: Boolean }) public required?: boolean;
|
||||||
|
|
||||||
@property({ type: Boolean }) public enableMillisecond?: boolean;
|
@property({ type: Boolean }) public enableMillisecond?: boolean;
|
||||||
|
37
src/components/ha-form/compute-initial-ha-form-data.ts
Normal file
37
src/components/ha-form/compute-initial-ha-form-data.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { HaFormSchema } from "./types";
|
||||||
|
|
||||||
|
export const computeInitialHaFormData = (
|
||||||
|
schema: HaFormSchema[]
|
||||||
|
): Record<string, any> => {
|
||||||
|
const data = {};
|
||||||
|
schema.forEach((field) => {
|
||||||
|
if (field.description?.suggested_value) {
|
||||||
|
data[field.name] = field.description.suggested_value;
|
||||||
|
} else if ("default" in field) {
|
||||||
|
data[field.name] = field.default;
|
||||||
|
} else if (!field.required) {
|
||||||
|
// Do nothing.
|
||||||
|
} else if (field.type === "boolean") {
|
||||||
|
data[field.name] = false;
|
||||||
|
} else if (field.type === "string") {
|
||||||
|
data[field.name] = "";
|
||||||
|
} else if (field.type === "integer") {
|
||||||
|
data[field.name] = "valueMin" in field ? field.valueMin : 0;
|
||||||
|
} else if (field.type === "constant") {
|
||||||
|
data[field.name] = field.value;
|
||||||
|
} else if (field.type === "float") {
|
||||||
|
data[field.name] = 0.0;
|
||||||
|
} else if (field.type === "select") {
|
||||||
|
if (field.options.length) {
|
||||||
|
data[field.name] = field.options[0][0];
|
||||||
|
}
|
||||||
|
} else if (field.type === "positive_time_period_dict") {
|
||||||
|
data[field.name] = {
|
||||||
|
hours: 0,
|
||||||
|
minutes: 0,
|
||||||
|
seconds: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
};
|
@ -1,13 +1,14 @@
|
|||||||
import "@polymer/paper-checkbox/paper-checkbox";
|
import "@material/mwc-formfield";
|
||||||
import type { PaperCheckboxElement } from "@polymer/paper-checkbox/paper-checkbox";
|
import { html, LitElement, TemplateResult } from "lit";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import type {
|
import type {
|
||||||
HaFormBooleanData,
|
HaFormBooleanData,
|
||||||
HaFormBooleanSchema,
|
HaFormBooleanSchema,
|
||||||
HaFormElement,
|
HaFormElement,
|
||||||
} from "./ha-form";
|
} from "./types";
|
||||||
|
import type { HaCheckbox } from "../ha-checkbox";
|
||||||
|
import "../ha-checkbox";
|
||||||
|
|
||||||
@customElement("ha-form-boolean")
|
@customElement("ha-form-boolean")
|
||||||
export class HaFormBoolean extends LitElement implements HaFormElement {
|
export class HaFormBoolean extends LitElement implements HaFormElement {
|
||||||
@ -17,8 +18,6 @@ export class HaFormBoolean extends LitElement implements HaFormElement {
|
|||||||
|
|
||||||
@property() public label!: string;
|
@property() public label!: string;
|
||||||
|
|
||||||
@property() public suffix!: string;
|
|
||||||
|
|
||||||
@query("paper-checkbox", true) private _input?: HTMLElement;
|
@query("paper-checkbox", true) private _input?: HTMLElement;
|
||||||
|
|
||||||
public focus() {
|
public focus() {
|
||||||
@ -29,26 +28,20 @@ export class HaFormBoolean extends LitElement implements HaFormElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<paper-checkbox .checked=${this.data} @change=${this._valueChanged}>
|
<mwc-formfield .label=${this.label}>
|
||||||
${this.label}
|
<ha-checkbox
|
||||||
</paper-checkbox>
|
.checked=${this.data}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-checkbox>
|
||||||
|
</mwc-formfield>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _valueChanged(ev: Event) {
|
private _valueChanged(ev: Event) {
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: (ev.target as PaperCheckboxElement).checked,
|
value: (ev.target as HaCheckbox).checked,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return css`
|
|
||||||
paper-checkbox {
|
|
||||||
display: block;
|
|
||||||
padding: 22px 0;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -1,14 +1,6 @@
|
|||||||
import {
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
PropertyValues,
|
|
||||||
TemplateResult,
|
|
||||||
} from "lit";
|
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { HaFormConstantSchema, HaFormElement } from "./types";
|
||||||
import { HaFormConstantSchema, HaFormElement } from "./ha-form";
|
|
||||||
|
|
||||||
@customElement("ha-form-constant")
|
@customElement("ha-form-constant")
|
||||||
export class HaFormConstant extends LitElement implements HaFormElement {
|
export class HaFormConstant extends LitElement implements HaFormElement {
|
||||||
@ -16,13 +8,6 @@ export class HaFormConstant extends LitElement implements HaFormElement {
|
|||||||
|
|
||||||
@property() public label!: string;
|
@property() public label!: string;
|
||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues) {
|
|
||||||
super.firstUpdated(changedProps);
|
|
||||||
fireEvent(this, "value-changed", {
|
|
||||||
value: this.schema.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`<span class="label">${this.label}</span>: ${this.schema.value}`;
|
return html`<span class="label">${this.label}</span>: ${this.schema.value}`;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
import "@material/mwc-textfield";
|
||||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
import type { TextField } from "@material/mwc-textfield";
|
||||||
import { html, LitElement, TemplateResult } from "lit";
|
import { css, html, LitElement, TemplateResult, PropertyValues } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { HaFormElement, HaFormFloatData, HaFormFloatSchema } from "./ha-form";
|
import { HaFormElement, HaFormFloatData, HaFormFloatSchema } from "./types";
|
||||||
|
|
||||||
@customElement("ha-form-float")
|
@customElement("ha-form-float")
|
||||||
export class HaFormFloat extends LitElement implements HaFormElement {
|
export class HaFormFloat extends LitElement implements HaFormElement {
|
||||||
@ -13,9 +13,7 @@ export class HaFormFloat extends LitElement implements HaFormElement {
|
|||||||
|
|
||||||
@property() public label!: string;
|
@property() public label!: string;
|
||||||
|
|
||||||
@property() public suffix!: string;
|
@query("mwc-textfield") private _input?: HTMLElement;
|
||||||
|
|
||||||
@query("paper-input", true) private _input?: HTMLElement;
|
|
||||||
|
|
||||||
public focus() {
|
public focus() {
|
||||||
if (this._input) {
|
if (this._input) {
|
||||||
@ -25,33 +23,58 @@ export class HaFormFloat extends LitElement implements HaFormElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<paper-input
|
<mwc-textfield
|
||||||
.label=${this.label}
|
.label=${this.label}
|
||||||
.value=${this._value}
|
.value=${this.data !== undefined ? this.data : ""}
|
||||||
.required=${this.schema.required}
|
.required=${this.schema.required}
|
||||||
.autoValidate=${this.schema.required}
|
.autoValidate=${this.schema.required}
|
||||||
@value-changed=${this._valueChanged}
|
.suffix=${this.schema.description?.suffix}
|
||||||
>
|
.validationMessage=${this.schema.required ? "Required" : undefined}
|
||||||
<span suffix slot="suffix">${this.suffix}</span>
|
@input=${this._valueChanged}
|
||||||
</paper-input>
|
></mwc-textfield>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _value() {
|
protected updated(changedProps: PropertyValues): void {
|
||||||
return this.data;
|
if (changedProps.has("schema")) {
|
||||||
|
this.toggleAttribute("own-margin", !!this.schema.required);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _valueChanged(ev: Event) {
|
private _valueChanged(ev: Event) {
|
||||||
const value: number | undefined = (ev.target as PaperInputElement).value
|
const source = ev.target as TextField;
|
||||||
? Number((ev.target as PaperInputElement).value)
|
const rawValue = source.value;
|
||||||
: undefined;
|
|
||||||
if (this._value === value) {
|
let value: number | undefined;
|
||||||
|
|
||||||
|
if (rawValue !== "") {
|
||||||
|
value = parseFloat(rawValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect anything changed
|
||||||
|
if (this.data === value) {
|
||||||
|
// parseFloat will drop invalid text at the end, in that case update textfield
|
||||||
|
const newRawValue = value === undefined ? "" : String(value);
|
||||||
|
if (source.value !== newRawValue) {
|
||||||
|
source.value = newRawValue;
|
||||||
|
return;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
:host([own-margin]) {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
mwc-textfield {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
import "@material/mwc-textfield";
|
||||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
import type { TextField } from "@material/mwc-textfield";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import "@material/mwc-slider";
|
||||||
|
import type { Slider } from "@material/mwc-slider";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResultGroup,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
PropertyValues,
|
||||||
|
} from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { HaCheckbox } from "../ha-checkbox";
|
import { HaCheckbox } from "../ha-checkbox";
|
||||||
import "../ha-slider";
|
import { HaFormElement, HaFormIntegerData, HaFormIntegerSchema } from "./types";
|
||||||
import type { HaSlider } from "../ha-slider";
|
|
||||||
import {
|
|
||||||
HaFormElement,
|
|
||||||
HaFormIntegerData,
|
|
||||||
HaFormIntegerSchema,
|
|
||||||
} from "./ha-form";
|
|
||||||
|
|
||||||
@customElement("ha-form-integer")
|
@customElement("ha-form-integer")
|
||||||
export class HaFormInteger extends LitElement implements HaFormElement {
|
export class HaFormInteger extends LitElement implements HaFormElement {
|
||||||
@ -20,10 +23,10 @@ export class HaFormInteger extends LitElement implements HaFormElement {
|
|||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
|
|
||||||
@property() public suffix?: string;
|
|
||||||
|
|
||||||
@query("paper-input ha-slider") private _input?: HTMLElement;
|
@query("paper-input ha-slider") private _input?: HTMLElement;
|
||||||
|
|
||||||
|
private _lastValue?: HaFormIntegerData;
|
||||||
|
|
||||||
public focus() {
|
public focus() {
|
||||||
if (this._input) {
|
if (this._input) {
|
||||||
this._input.focus();
|
this._input.focus();
|
||||||
@ -31,66 +34,112 @@ export class HaFormInteger extends LitElement implements HaFormElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return "valueMin" in this.schema && "valueMax" in this.schema
|
if ("valueMin" in this.schema && "valueMax" in this.schema) {
|
||||||
? html`
|
return html`
|
||||||
<div>
|
<div>
|
||||||
${this.label}
|
${this.label}
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
${this.schema.optional && this.schema.default === undefined
|
${this.schema.optional
|
||||||
? html`
|
? html`
|
||||||
<ha-checkbox
|
<ha-checkbox
|
||||||
@change=${this._handleCheckboxChange}
|
@change=${this._handleCheckboxChange}
|
||||||
.checked=${this.data !== undefined}
|
.checked=${this.data !== undefined}
|
||||||
></ha-checkbox>
|
></ha-checkbox>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-slider
|
<mwc-slider
|
||||||
pin
|
discrete
|
||||||
editable
|
.value=${this._value}
|
||||||
.value=${this._value}
|
.min=${this.schema.valueMin}
|
||||||
.min=${this.schema.valueMin}
|
.max=${this.schema.valueMax}
|
||||||
.max=${this.schema.valueMax}
|
.disabled=${this.data === undefined && this.schema.optional}
|
||||||
.disabled=${this.data === undefined &&
|
@change=${this._valueChanged}
|
||||||
this.schema.optional &&
|
></mwc-slider>
|
||||||
this.schema.default === undefined}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
></ha-slider>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
`
|
</div>
|
||||||
: html`
|
`;
|
||||||
<paper-input
|
}
|
||||||
type="number"
|
|
||||||
.label=${this.label}
|
return html`
|
||||||
.value=${this._value}
|
<mwc-textfield
|
||||||
.required=${this.schema.required}
|
type="number"
|
||||||
.autoValidate=${this.schema.required}
|
.label=${this.label}
|
||||||
@value-changed=${this._valueChanged}
|
.value=${this.data !== undefined ? this.data : ""}
|
||||||
></paper-input>
|
.required=${this.schema.required}
|
||||||
`;
|
.autoValidate=${this.schema.required}
|
||||||
|
.suffix=${this.schema.description?.suffix}
|
||||||
|
.validationMessage=${this.schema.required ? "Required" : undefined}
|
||||||
|
@input=${this._valueChanged}
|
||||||
|
></mwc-textfield>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps: PropertyValues): void {
|
||||||
|
if (changedProps.has("schema")) {
|
||||||
|
this.toggleAttribute(
|
||||||
|
"own-margin",
|
||||||
|
!("valueMin" in this.schema && "valueMax" in this.schema) &&
|
||||||
|
!!this.schema.required
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _value() {
|
private get _value() {
|
||||||
return (
|
if (this.data !== undefined) {
|
||||||
this.data ||
|
return this.data;
|
||||||
this.schema.description?.suggested_value ||
|
}
|
||||||
this.schema.default ||
|
|
||||||
0
|
if (this.schema.optional) {
|
||||||
);
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.schema.description?.suggested_value || this.schema.default || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleCheckboxChange(ev: Event) {
|
private _handleCheckboxChange(ev: Event) {
|
||||||
const checked = (ev.target as HaCheckbox).checked;
|
const checked = (ev.target as HaCheckbox).checked;
|
||||||
|
let value: HaFormIntegerData | undefined;
|
||||||
|
if (checked) {
|
||||||
|
for (const candidate of [
|
||||||
|
this._lastValue,
|
||||||
|
this.schema.description?.suggested_value as HaFormIntegerData,
|
||||||
|
this.schema.default,
|
||||||
|
0,
|
||||||
|
]) {
|
||||||
|
if (candidate !== undefined) {
|
||||||
|
value = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We track last value so user can disable and enable a field without losing
|
||||||
|
// their value.
|
||||||
|
this._lastValue = this.data;
|
||||||
|
}
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: checked ? this._value : undefined,
|
value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _valueChanged(ev: Event) {
|
private _valueChanged(ev: Event) {
|
||||||
const value = Number((ev.target as PaperInputElement | HaSlider).value);
|
const source = ev.target as TextField | Slider;
|
||||||
if (this._value === value) {
|
const rawValue = source.value;
|
||||||
|
|
||||||
|
let value: number | undefined;
|
||||||
|
|
||||||
|
if (rawValue !== "") {
|
||||||
|
value = parseInt(String(rawValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.data === value) {
|
||||||
|
// parseInt will drop invalid text at the end, in that case update textfield
|
||||||
|
const newRawValue = value === undefined ? "" : String(value);
|
||||||
|
if (source.value !== newRawValue) {
|
||||||
|
source.value = newRawValue;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
@ -98,12 +147,17 @@ export class HaFormInteger extends LitElement implements HaFormElement {
|
|||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
|
:host([own-margin]) {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
.flex {
|
.flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
ha-slider {
|
mwc-slider {
|
||||||
width: 100%;
|
flex: 1;
|
||||||
margin-right: 16px;
|
}
|
||||||
|
mwc-textfield {
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,35 @@
|
|||||||
import { mdiMenuDown } from "@mdi/js";
|
import { mdiMenuDown, mdiMenuUp } from "@mdi/js";
|
||||||
import "@polymer/paper-checkbox/paper-checkbox";
|
import "@material/mwc-textfield";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@material/mwc-formfield";
|
||||||
import "@polymer/paper-item/paper-icon-item";
|
import {
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
css,
|
||||||
import "@polymer/paper-menu-button/paper-menu-button";
|
CSSResultGroup,
|
||||||
import "@polymer/paper-ripple/paper-ripple";
|
html,
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
LitElement,
|
||||||
import { customElement, property, state, query } from "lit/decorators";
|
TemplateResult,
|
||||||
|
PropertyValues,
|
||||||
|
} from "lit";
|
||||||
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import "../ha-svg-icon";
|
import "../ha-button-menu";
|
||||||
|
import "../ha-icon";
|
||||||
import {
|
import {
|
||||||
HaFormElement,
|
HaFormElement,
|
||||||
HaFormMultiSelectData,
|
HaFormMultiSelectData,
|
||||||
HaFormMultiSelectSchema,
|
HaFormMultiSelectSchema,
|
||||||
} from "./ha-form";
|
} from "./types";
|
||||||
|
import "../ha-checkbox";
|
||||||
|
import type { HaCheckbox } from "../ha-checkbox";
|
||||||
|
|
||||||
|
function optionValue(item: string | string[]): string {
|
||||||
|
return Array.isArray(item) ? item[0] : item;
|
||||||
|
}
|
||||||
|
|
||||||
|
function optionLabel(item: string | string[]): string {
|
||||||
|
return Array.isArray(item) ? item[1] || item[0] : item;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SHOW_ALL_ENTRIES_LIMIT = 6;
|
||||||
|
|
||||||
@customElement("ha-form-multi_select")
|
@customElement("ha-form-multi_select")
|
||||||
export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
||||||
@ -23,9 +39,7 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
|||||||
|
|
||||||
@property() public label!: string;
|
@property() public label!: string;
|
||||||
|
|
||||||
@property() public suffix!: string;
|
@state() private _opened = false;
|
||||||
|
|
||||||
@state() private _init = false;
|
|
||||||
|
|
||||||
@query("paper-menu-button", true) private _input?: HTMLElement;
|
@query("paper-menu-button", true) private _input?: HTMLElement;
|
||||||
|
|
||||||
@ -36,118 +50,136 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const options = Array.isArray(this.schema.options)
|
const options = Object.entries(this.schema.options);
|
||||||
? this.schema.options
|
|
||||||
: Object.entries(this.schema.options!);
|
|
||||||
|
|
||||||
const data = this.data || [];
|
const data = this.data || [];
|
||||||
|
|
||||||
|
const renderedOptions = options.map((item: string | [string, string]) => {
|
||||||
|
const value = optionValue(item);
|
||||||
|
return html`
|
||||||
|
<mwc-formfield .label=${optionLabel(item)}>
|
||||||
|
<ha-checkbox
|
||||||
|
.checked=${data.includes(value)}
|
||||||
|
.value=${value}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-checkbox>
|
||||||
|
</mwc-formfield>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// We will just render all checkboxes.
|
||||||
|
if (options.length < SHOW_ALL_ENTRIES_LIMIT) {
|
||||||
|
return html`<div>${this.label}${renderedOptions}</div> `;
|
||||||
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<paper-menu-button horizontal-align="right" vertical-offset="8">
|
<ha-button-menu
|
||||||
<div class="dropdown-trigger" slot="dropdown-trigger">
|
fixed
|
||||||
<paper-ripple></paper-ripple>
|
corner="BOTTOM_START"
|
||||||
<paper-input
|
@opened=${this._handleOpen}
|
||||||
id="input"
|
@closed=${this._handleClose}
|
||||||
type="text"
|
>
|
||||||
readonly
|
<mwc-textfield
|
||||||
value=${data
|
slot="trigger"
|
||||||
.map((value) => this.schema.options![value] || value)
|
.label=${this.label}
|
||||||
.join(", ")}
|
.value=${data
|
||||||
label=${this.label}
|
.map((value) => this.schema.options![value] || value)
|
||||||
input-role="button"
|
.join(", ")}
|
||||||
input-aria-haspopup="listbox"
|
tabindex="-1"
|
||||||
autocomplete="off"
|
></mwc-textfield>
|
||||||
>
|
<ha-svg-icon
|
||||||
<ha-svg-icon
|
slot="trigger"
|
||||||
.path=${mdiMenuDown}
|
.path=${this._opened ? mdiMenuUp : mdiMenuDown}
|
||||||
suffix
|
></ha-svg-icon>
|
||||||
slot="suffix"
|
${renderedOptions}
|
||||||
></ha-svg-icon>
|
</ha-button-menu>
|
||||||
</paper-input>
|
|
||||||
</div>
|
|
||||||
<paper-listbox
|
|
||||||
multi
|
|
||||||
slot="dropdown-content"
|
|
||||||
attr-for-selected="item-value"
|
|
||||||
.selectedValues=${data}
|
|
||||||
@selected-items-changed=${this._valueChanged}
|
|
||||||
@iron-select=${this._onSelect}
|
|
||||||
>
|
|
||||||
${
|
|
||||||
// TS doesn't work with union array types https://github.com/microsoft/TypeScript/issues/36390
|
|
||||||
// @ts-ignore
|
|
||||||
options.map((item: string | [string, string]) => {
|
|
||||||
const value = this._optionValue(item);
|
|
||||||
return html`
|
|
||||||
<paper-icon-item .itemValue=${value}>
|
|
||||||
<paper-checkbox
|
|
||||||
.checked=${data.includes(value)}
|
|
||||||
slot="item-icon"
|
|
||||||
></paper-checkbox>
|
|
||||||
${this._optionLabel(item)}
|
|
||||||
</paper-icon-item>
|
|
||||||
`;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-menu-button>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated() {
|
protected firstUpdated() {
|
||||||
this.updateComplete.then(() => {
|
this.updateComplete.then(() => {
|
||||||
const input = (
|
const { formElement, mdcRoot } =
|
||||||
this.shadowRoot?.querySelector("paper-input")?.inputElement as any
|
this.shadowRoot?.querySelector("mwc-textfield") || ({} as any);
|
||||||
)?.inputElement;
|
if (formElement) {
|
||||||
if (input) {
|
formElement.style.textOverflow = "ellipsis";
|
||||||
input.style.textOverflow = "ellipsis";
|
formElement.style.cursor = "pointer";
|
||||||
|
formElement.setAttribute("readonly", "");
|
||||||
|
}
|
||||||
|
if (mdcRoot) {
|
||||||
|
mdcRoot.style.cursor = "pointer";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _optionValue(item: string | string[]): string {
|
protected updated(changedProps: PropertyValues): void {
|
||||||
return Array.isArray(item) ? item[0] : item;
|
if (changedProps.has("schema")) {
|
||||||
}
|
this.toggleAttribute(
|
||||||
|
"own-margin",
|
||||||
private _optionLabel(item: string | string[]): string {
|
Object.keys(this.schema.options).length >= SHOW_ALL_ENTRIES_LIMIT &&
|
||||||
return Array.isArray(item) ? item[1] || item[0] : item;
|
!!this.schema.required
|
||||||
}
|
);
|
||||||
|
}
|
||||||
private _onSelect(ev: Event) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _valueChanged(ev: CustomEvent): void {
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
if (!ev.detail.value || !this._init) {
|
const { value, checked } = ev.target as HaCheckbox;
|
||||||
// ignore first call because that is the init of the component
|
|
||||||
this._init = true;
|
let newValue: string[];
|
||||||
return;
|
|
||||||
|
if (checked) {
|
||||||
|
if (!this.data) {
|
||||||
|
newValue = [value];
|
||||||
|
} else if (this.data.includes(value)) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
newValue = [...this.data, value];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!this.data.includes(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newValue = this.data.filter((v) => v !== value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fireEvent(
|
fireEvent(this, "value-changed", {
|
||||||
this,
|
value: newValue,
|
||||||
"value-changed",
|
});
|
||||||
{
|
}
|
||||||
value: ev.detail.value.map((element) => element.itemValue),
|
|
||||||
},
|
private _handleOpen(ev: Event): void {
|
||||||
{ bubbles: false }
|
ev.stopPropagation();
|
||||||
);
|
this._opened = true;
|
||||||
|
this.toggleAttribute("opened", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleClose(ev: Event): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._opened = false;
|
||||||
|
this.toggleAttribute("opened", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
paper-menu-button {
|
:host([own-margin]) {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
ha-button-menu,
|
||||||
|
mwc-textfield,
|
||||||
|
mwc-formfield {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 0;
|
|
||||||
--paper-item-icon-width: 34px;
|
|
||||||
}
|
}
|
||||||
paper-ripple {
|
ha-svg-icon {
|
||||||
top: 12px;
|
color: var(--input-dropdown-icon-color);
|
||||||
left: 0px;
|
position: absolute;
|
||||||
bottom: 8px;
|
right: 1em;
|
||||||
right: 0px;
|
top: 1em;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
paper-input {
|
:host([opened]) ha-svg-icon {
|
||||||
text-overflow: ellipsis;
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
:host([opened]) ha-button-menu {
|
||||||
|
--mdc-text-field-idle-line-color: var(--input-hover-line-color);
|
||||||
|
--mdc-text-field-label-ink-color: var(--primary-color);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { html, LitElement, TemplateResult } from "lit";
|
import { html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import "../ha-duration-input";
|
import "../ha-duration-input";
|
||||||
import { HaFormElement, HaFormTimeData, HaFormTimeSchema } from "./ha-form";
|
import { HaFormElement, HaFormTimeData, HaFormTimeSchema } from "./types";
|
||||||
|
|
||||||
@customElement("ha-form-positive_time_period_dict")
|
@customElement("ha-form-positive_time_period_dict")
|
||||||
export class HaFormTimePeriod extends LitElement implements HaFormElement {
|
export class HaFormTimePeriod extends LitElement implements HaFormElement {
|
||||||
@ -11,8 +11,6 @@ export class HaFormTimePeriod extends LitElement implements HaFormElement {
|
|||||||
|
|
||||||
@property() public label!: string;
|
@property() public label!: string;
|
||||||
|
|
||||||
@property() public suffix!: string;
|
|
||||||
|
|
||||||
@query("ha-time-input", true) private _input?: HTMLElement;
|
@query("ha-time-input", true) private _input?: HTMLElement;
|
||||||
|
|
||||||
public focus() {
|
public focus() {
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import "@material/mwc-icon-button/mwc-icon-button";
|
import "@material/mwc-select";
|
||||||
import { mdiClose, mdiMenuDown } from "@mdi/js";
|
import type { Select } from "@material/mwc-select";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import "@polymer/paper-menu-button/paper-menu-button";
|
|
||||||
import "@polymer/paper-ripple/paper-ripple";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import "../ha-svg-icon";
|
import "../ha-svg-icon";
|
||||||
import { HaFormElement, HaFormSelectData, HaFormSelectSchema } from "./ha-form";
|
import "../ha-radio";
|
||||||
|
import { HaFormElement, HaFormSelectData, HaFormSelectSchema } from "./types";
|
||||||
|
|
||||||
|
import { stopPropagation } from "../../common/dom/stop_propagation";
|
||||||
|
import type { HaRadio } from "../ha-radio";
|
||||||
|
|
||||||
@customElement("ha-form-select")
|
@customElement("ha-form-select")
|
||||||
export class HaFormSelect extends LitElement implements HaFormElement {
|
export class HaFormSelect extends LitElement implements HaFormElement {
|
||||||
@ -19,9 +19,7 @@ export class HaFormSelect extends LitElement implements HaFormElement {
|
|||||||
|
|
||||||
@property() public label!: string;
|
@property() public label!: string;
|
||||||
|
|
||||||
@property() public suffix!: string;
|
@query("mwc-select", true) private _input?: HTMLElement;
|
||||||
|
|
||||||
@query("ha-paper-dropdown-menu", true) private _input?: HTMLElement;
|
|
||||||
|
|
||||||
public focus() {
|
public focus() {
|
||||||
if (this._input) {
|
if (this._input) {
|
||||||
@ -30,90 +28,67 @@ export class HaFormSelect extends LitElement implements HaFormElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
if (!this.schema.optional && this.schema.options!.length < 6) {
|
||||||
<paper-menu-button horizontal-align="right" vertical-offset="8">
|
return html`
|
||||||
<div class="dropdown-trigger" slot="dropdown-trigger">
|
<div>
|
||||||
<paper-ripple></paper-ripple>
|
${this.label}
|
||||||
<paper-input
|
${this.schema.options.map(
|
||||||
id="input"
|
([value, label]) => html`
|
||||||
type="text"
|
<mwc-formfield .label=${label}>
|
||||||
readonly
|
<ha-radio
|
||||||
value=${this.data}
|
.checked=${value === this.data}
|
||||||
label=${this.label}
|
.value=${value}
|
||||||
input-role="button"
|
@change=${this._valueChanged}
|
||||||
input-aria-haspopup="listbox"
|
></ha-radio>
|
||||||
autocomplete="off"
|
</mwc-formfield>
|
||||||
>
|
`
|
||||||
${this.data && this.schema.optional
|
)}
|
||||||
? html`<mwc-icon-button
|
|
||||||
slot="suffix"
|
|
||||||
class="clear-button"
|
|
||||||
@click=${this._clearValue}
|
|
||||||
>
|
|
||||||
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
|
||||||
</mwc-icon-button>`
|
|
||||||
: ""}
|
|
||||||
<mwc-icon-button slot="suffix">
|
|
||||||
<ha-svg-icon .path=${mdiMenuDown}></ha-svg-icon>
|
|
||||||
</mwc-icon-button>
|
|
||||||
</paper-input>
|
|
||||||
</div>
|
</div>
|
||||||
<paper-listbox
|
`;
|
||||||
slot="dropdown-content"
|
}
|
||||||
attr-for-selected="item-value"
|
|
||||||
.selected=${this.data}
|
return html`
|
||||||
@selected-item-changed=${this._valueChanged}
|
<mwc-select
|
||||||
>
|
fixedMenuPosition
|
||||||
${
|
.label=${this.label}
|
||||||
// TS doesn't work with union array types https://github.com/microsoft/TypeScript/issues/36390
|
.value=${this.data}
|
||||||
// @ts-ignore
|
@closed=${stopPropagation}
|
||||||
this.schema.options!.map(
|
@selected=${this._valueChanged}
|
||||||
(item: string | [string, string]) => html`
|
>
|
||||||
<paper-item .itemValue=${this._optionValue(item)}>
|
${this.schema.optional
|
||||||
${this._optionLabel(item)}
|
? html`<mwc-list-item value=""></mwc-list-item>`
|
||||||
</paper-item>
|
: ""}
|
||||||
`
|
${this.schema.options!.map(
|
||||||
)
|
([value, label]) => html`
|
||||||
}
|
<mwc-list-item .value=${value}>${label}</mwc-list-item>
|
||||||
</paper-listbox>
|
`
|
||||||
</paper-menu-button>
|
)}
|
||||||
|
</mwc-select>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _optionValue(item: string | [string, string]) {
|
|
||||||
return Array.isArray(item) ? item[0] : item;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _optionLabel(item: string | [string, string]) {
|
|
||||||
return Array.isArray(item) ? item[1] || item[0] : item;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _clearValue(ev: CustomEvent) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
fireEvent(this, "value-changed", { value: undefined });
|
|
||||||
}
|
|
||||||
|
|
||||||
private _valueChanged(ev: CustomEvent) {
|
private _valueChanged(ev: CustomEvent) {
|
||||||
if (!ev.detail.value) {
|
ev.stopPropagation();
|
||||||
|
let value: string | undefined = (ev.target as Select | HaRadio).value;
|
||||||
|
|
||||||
|
if (value === this.data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value === "") {
|
||||||
|
value = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: ev.detail.value.itemValue,
|
value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
paper-menu-button {
|
mwc-select,
|
||||||
|
mwc-formfield {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
paper-input > mwc-icon-button {
|
|
||||||
--mdc-icon-button-size: 24px;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
.clear-button {
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
import "@material/mwc-icon-button/mwc-icon-button";
|
|
||||||
import { mdiEye, mdiEyeOff } from "@mdi/js";
|
import { mdiEye, mdiEyeOff } from "@mdi/js";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@material/mwc-textfield";
|
||||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
import type { TextField } from "@material/mwc-textfield";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import {
|
||||||
|
css,
|
||||||
|
CSSResultGroup,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
PropertyValues,
|
||||||
|
} from "lit";
|
||||||
import { customElement, property, state, query } from "lit/decorators";
|
import { customElement, property, state, query } from "lit/decorators";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import "../ha-svg-icon";
|
import "../ha-svg-icon";
|
||||||
@ -10,7 +16,7 @@ import type {
|
|||||||
HaFormElement,
|
HaFormElement,
|
||||||
HaFormStringData,
|
HaFormStringData,
|
||||||
HaFormStringSchema,
|
HaFormStringSchema,
|
||||||
} from "./ha-form";
|
} from "./types";
|
||||||
|
|
||||||
const MASKED_FIELDS = ["password", "secret", "token"];
|
const MASKED_FIELDS = ["password", "secret", "token"];
|
||||||
|
|
||||||
@ -22,11 +28,9 @@ export class HaFormString extends LitElement implements HaFormElement {
|
|||||||
|
|
||||||
@property() public label!: string;
|
@property() public label!: string;
|
||||||
|
|
||||||
@property() public suffix!: string;
|
|
||||||
|
|
||||||
@state() private _unmaskedPassword = false;
|
@state() private _unmaskedPassword = false;
|
||||||
|
|
||||||
@query("paper-input") private _input?: HTMLElement;
|
@query("mwc-textfield") private _input?: HTMLElement;
|
||||||
|
|
||||||
public focus(): void {
|
public focus(): void {
|
||||||
if (this._input) {
|
if (this._input) {
|
||||||
@ -35,20 +39,31 @@ export class HaFormString extends LitElement implements HaFormElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return MASKED_FIELDS.some((field) => this.schema.name.includes(field))
|
const isPassword = MASKED_FIELDS.some((field) =>
|
||||||
? html`
|
this.schema.name.includes(field)
|
||||||
<paper-input
|
);
|
||||||
.type=${this._unmaskedPassword ? "text" : "password"}
|
return html`
|
||||||
.label=${this.label}
|
<mwc-textfield
|
||||||
.value=${this.data}
|
.type=${!isPassword
|
||||||
.required=${this.schema.required}
|
? this._stringType
|
||||||
.autoValidate=${this.schema.required}
|
: this._unmaskedPassword
|
||||||
@value-changed=${this._valueChanged}
|
? "text"
|
||||||
>
|
: "password"}
|
||||||
|
.label=${this.label}
|
||||||
|
.value=${this.data || ""}
|
||||||
|
.required=${this.schema.required}
|
||||||
|
.autoValidate=${this.schema.required}
|
||||||
|
.suffix=${isPassword
|
||||||
|
? // reserve some space for the icon.
|
||||||
|
html`<div style="width: 24px"></div>`
|
||||||
|
: this.schema.description?.suffix}
|
||||||
|
.validationMessage=${this.schema.required ? "Required" : undefined}
|
||||||
|
@input=${this._valueChanged}
|
||||||
|
></mwc-textfield>
|
||||||
|
${isPassword
|
||||||
|
? html`
|
||||||
<mwc-icon-button
|
<mwc-icon-button
|
||||||
toggles
|
toggles
|
||||||
slot="suffix"
|
|
||||||
id="iconButton"
|
|
||||||
title="Click to toggle between masked and clear password"
|
title="Click to toggle between masked and clear password"
|
||||||
@click=${this._toggleUnmaskedPassword}
|
@click=${this._toggleUnmaskedPassword}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
@ -56,19 +71,15 @@ export class HaFormString extends LitElement implements HaFormElement {
|
|||||||
.path=${this._unmaskedPassword ? mdiEyeOff : mdiEye}
|
.path=${this._unmaskedPassword ? mdiEyeOff : mdiEye}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
</paper-input>
|
`
|
||||||
`
|
: ""}
|
||||||
: html`
|
`;
|
||||||
<paper-input
|
}
|
||||||
.type=${this._stringType}
|
|
||||||
.label=${this.label}
|
protected updated(changedProps: PropertyValues): void {
|
||||||
.value=${this.data}
|
if (changedProps.has("schema")) {
|
||||||
.required=${this.schema.required}
|
this.toggleAttribute("own-margin", !!this.schema.required);
|
||||||
.autoValidate=${this.schema.required}
|
}
|
||||||
error-message="Required"
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
></paper-input>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleUnmaskedPassword(): void {
|
private _toggleUnmaskedPassword(): void {
|
||||||
@ -76,10 +87,13 @@ export class HaFormString extends LitElement implements HaFormElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _valueChanged(ev: Event): void {
|
private _valueChanged(ev: Event): void {
|
||||||
const value = (ev.target as PaperInputElement).value;
|
let value: string | undefined = (ev.target as TextField).value;
|
||||||
if (this.data === value) {
|
if (this.data === value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (value === "" && this.schema.optional) {
|
||||||
|
value = undefined;
|
||||||
|
}
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
@ -99,7 +113,20 @@ export class HaFormString extends LitElement implements HaFormElement {
|
|||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
:host([own-margin]) {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
mwc-textfield {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
mwc-icon-button {
|
mwc-icon-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 1em;
|
||||||
|
right: 12px;
|
||||||
--mdc-icon-button-size: 24px;
|
--mdc-icon-button-size: 24px;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { css, CSSResultGroup, html, LitElement } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
|
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { HaDurationData } from "../ha-duration-input";
|
import "../ha-alert";
|
||||||
import "./ha-form-boolean";
|
import "./ha-form-boolean";
|
||||||
import "./ha-form-constant";
|
import "./ha-form-constant";
|
||||||
import "./ha-form-float";
|
import "./ha-form-float";
|
||||||
@ -11,160 +11,79 @@ import "./ha-form-multi_select";
|
|||||||
import "./ha-form-positive_time_period_dict";
|
import "./ha-form-positive_time_period_dict";
|
||||||
import "./ha-form-select";
|
import "./ha-form-select";
|
||||||
import "./ha-form-string";
|
import "./ha-form-string";
|
||||||
|
import { HaFormElement, HaFormDataContainer, HaFormSchema } from "./types";
|
||||||
|
|
||||||
export type HaFormSchema =
|
const getValue = (obj, item) => (obj ? obj[item.name] : null);
|
||||||
| HaFormConstantSchema
|
|
||||||
| HaFormStringSchema
|
|
||||||
| HaFormIntegerSchema
|
|
||||||
| HaFormFloatSchema
|
|
||||||
| HaFormBooleanSchema
|
|
||||||
| HaFormSelectSchema
|
|
||||||
| HaFormMultiSelectSchema
|
|
||||||
| HaFormTimeSchema;
|
|
||||||
|
|
||||||
export interface HaFormBaseSchema {
|
|
||||||
name: string;
|
|
||||||
default?: HaFormData;
|
|
||||||
required?: boolean;
|
|
||||||
optional?: boolean;
|
|
||||||
description?: { suffix?: string; suggested_value?: HaFormData };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HaFormConstantSchema extends HaFormBaseSchema {
|
|
||||||
type: "constant";
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HaFormIntegerSchema extends HaFormBaseSchema {
|
|
||||||
type: "integer";
|
|
||||||
default?: HaFormIntegerData;
|
|
||||||
valueMin?: number;
|
|
||||||
valueMax?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HaFormSelectSchema extends HaFormBaseSchema {
|
|
||||||
type: "select";
|
|
||||||
options?: string[] | Array<[string, string]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HaFormMultiSelectSchema extends HaFormBaseSchema {
|
|
||||||
type: "multi_select";
|
|
||||||
options?: Record<string, string> | string[] | Array<[string, string]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HaFormFloatSchema extends HaFormBaseSchema {
|
|
||||||
type: "float";
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HaFormStringSchema extends HaFormBaseSchema {
|
|
||||||
type: "string";
|
|
||||||
format?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HaFormBooleanSchema extends HaFormBaseSchema {
|
|
||||||
type: "boolean";
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HaFormTimeSchema extends HaFormBaseSchema {
|
|
||||||
type: "positive_time_period_dict";
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HaFormDataContainer {
|
|
||||||
[key: string]: HaFormData;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type HaFormData =
|
|
||||||
| HaFormStringData
|
|
||||||
| HaFormIntegerData
|
|
||||||
| HaFormFloatData
|
|
||||||
| HaFormBooleanData
|
|
||||||
| HaFormSelectData
|
|
||||||
| HaFormMultiSelectData
|
|
||||||
| HaFormTimeData;
|
|
||||||
|
|
||||||
export type HaFormStringData = string;
|
|
||||||
export type HaFormIntegerData = number;
|
|
||||||
export type HaFormFloatData = number;
|
|
||||||
export type HaFormBooleanData = boolean;
|
|
||||||
export type HaFormSelectData = string;
|
|
||||||
export type HaFormMultiSelectData = string[];
|
|
||||||
export type HaFormTimeData = HaDurationData;
|
|
||||||
|
|
||||||
export interface HaFormElement extends LitElement {
|
|
||||||
schema: HaFormSchema | HaFormSchema[];
|
|
||||||
data?: HaFormDataContainer | HaFormData;
|
|
||||||
label?: string;
|
|
||||||
suffix?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@customElement("ha-form")
|
@customElement("ha-form")
|
||||||
export class HaForm extends LitElement implements HaFormElement {
|
export class HaForm extends LitElement implements HaFormElement {
|
||||||
@property() public data!: HaFormDataContainer | HaFormData;
|
@property() public data!: HaFormDataContainer;
|
||||||
|
|
||||||
@property() public schema!: HaFormSchema | HaFormSchema[];
|
@property() public schema!: HaFormSchema[];
|
||||||
|
|
||||||
@property() public error;
|
@property() public error?: Record<string, string>;
|
||||||
|
|
||||||
@property() public computeError?: (schema: HaFormSchema, error) => string;
|
@property() public computeError?: (schema: HaFormSchema, error) => string;
|
||||||
|
|
||||||
@property() public computeLabel?: (schema: HaFormSchema) => string;
|
@property() public computeLabel?: (schema: HaFormSchema) => string;
|
||||||
|
|
||||||
@property() public computeSuffix?: (schema: HaFormSchema) => string;
|
|
||||||
|
|
||||||
public focus() {
|
public focus() {
|
||||||
const input =
|
const root = this.shadowRoot?.querySelector(".root");
|
||||||
this.shadowRoot!.getElementById("child-form") ||
|
if (!root) {
|
||||||
this.shadowRoot!.querySelector("ha-form");
|
|
||||||
if (!input) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(input as HTMLElement).focus();
|
for (const child of root.children) {
|
||||||
|
if (child.tagName !== "HA-ALERT") {
|
||||||
|
(child as HTMLElement).focus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (Array.isArray(this.schema)) {
|
return html`
|
||||||
return html`
|
<div class="root">
|
||||||
${this.error && this.error.base
|
${this.error && this.error.base
|
||||||
? html`
|
? html`
|
||||||
<div class="error">
|
<ha-alert alert-type="error">
|
||||||
${this._computeError(this.error.base, this.schema)}
|
${this._computeError(this.error.base, this.schema)}
|
||||||
</div>
|
</ha-alert>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.schema.map(
|
${this.schema.map((item) => {
|
||||||
(item) => html`
|
const error = getValue(this.error, item);
|
||||||
<ha-form
|
return html`
|
||||||
.data=${this._getValue(this.data, item)}
|
${error
|
||||||
.schema=${item}
|
? html`
|
||||||
.error=${this._getValue(this.error, item)}
|
<ha-alert own-margin alert-type="error">
|
||||||
@value-changed=${this._valueChanged}
|
${this._computeError(error, item)}
|
||||||
.computeError=${this.computeError}
|
</ha-alert>
|
||||||
.computeLabel=${this.computeLabel}
|
`
|
||||||
.computeSuffix=${this.computeSuffix}
|
: ""}
|
||||||
></ha-form>
|
${dynamicElement(`ha-form-${item.type}`, {
|
||||||
`
|
schema: item,
|
||||||
)}
|
data: getValue(this.data, item),
|
||||||
`;
|
label: this._computeLabel(item),
|
||||||
}
|
})}
|
||||||
|
`;
|
||||||
return html`
|
})}
|
||||||
${this.error
|
</div>
|
||||||
? html`
|
|
||||||
<div class="error">
|
|
||||||
${this._computeError(this.error, this.schema)}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${dynamicElement(`ha-form-${this.schema.type}`, {
|
|
||||||
schema: this.schema,
|
|
||||||
data: this.data,
|
|
||||||
label: this._computeLabel(this.schema),
|
|
||||||
suffix: this._computeSuffix(this.schema),
|
|
||||||
id: "child-form",
|
|
||||||
})}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected createRenderRoot() {
|
||||||
|
const root = super.createRenderRoot();
|
||||||
|
// attach it as soon as possible to make sure we fetch all events.
|
||||||
|
root.addEventListener("value-changed", (ev) => {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const schema = (ev.target as HaFormElement).schema as HaFormSchema;
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this.data, [schema.name]: ev.detail.value },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
private _computeLabel(schema: HaFormSchema) {
|
private _computeLabel(schema: HaFormSchema) {
|
||||||
return this.computeLabel
|
return this.computeLabel
|
||||||
? this.computeLabel(schema)
|
? this.computeLabel(schema)
|
||||||
@ -173,38 +92,25 @@ export class HaForm extends LitElement implements HaFormElement {
|
|||||||
: "";
|
: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeSuffix(schema: HaFormSchema) {
|
|
||||||
return this.computeSuffix
|
|
||||||
? this.computeSuffix(schema)
|
|
||||||
: schema && schema.description
|
|
||||||
? schema.description.suffix
|
|
||||||
: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private _computeError(error, schema: HaFormSchema | HaFormSchema[]) {
|
private _computeError(error, schema: HaFormSchema | HaFormSchema[]) {
|
||||||
return this.computeError ? this.computeError(error, schema) : error;
|
return this.computeError ? this.computeError(error, schema) : error;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getValue(obj, item) {
|
|
||||||
if (obj) {
|
|
||||||
return obj[item.name];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _valueChanged(ev: CustomEvent) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
const schema = (ev.target as HaFormElement).schema as HaFormSchema;
|
|
||||||
const data = this.data as HaFormDataContainer;
|
|
||||||
fireEvent(this, "value-changed", {
|
|
||||||
value: { ...data, [schema.name]: ev.detail.value },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
// .root has overflow: auto to avoid margin collapse
|
||||||
return css`
|
return css`
|
||||||
.error {
|
.root {
|
||||||
color: var(--error-color);
|
margin-bottom: -24px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.root > * {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.root > *:not([own-margin]) {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
ha-alert[own-margin] {
|
||||||
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
86
src/components/ha-form/types.ts
Normal file
86
src/components/ha-form/types.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import type { LitElement } from "lit";
|
||||||
|
import type { HaDurationData } from "../ha-duration-input";
|
||||||
|
|
||||||
|
export type HaFormSchema =
|
||||||
|
| HaFormConstantSchema
|
||||||
|
| HaFormStringSchema
|
||||||
|
| HaFormIntegerSchema
|
||||||
|
| HaFormFloatSchema
|
||||||
|
| HaFormBooleanSchema
|
||||||
|
| HaFormSelectSchema
|
||||||
|
| HaFormMultiSelectSchema
|
||||||
|
| HaFormTimeSchema;
|
||||||
|
|
||||||
|
export interface HaFormBaseSchema {
|
||||||
|
name: string;
|
||||||
|
default?: HaFormData;
|
||||||
|
required?: boolean;
|
||||||
|
optional?: boolean;
|
||||||
|
description?: { suffix?: string; suggested_value?: HaFormData };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HaFormConstantSchema extends HaFormBaseSchema {
|
||||||
|
type: "constant";
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HaFormIntegerSchema extends HaFormBaseSchema {
|
||||||
|
type: "integer";
|
||||||
|
default?: HaFormIntegerData;
|
||||||
|
valueMin?: number;
|
||||||
|
valueMax?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HaFormSelectSchema extends HaFormBaseSchema {
|
||||||
|
type: "select";
|
||||||
|
options: Array<[string, string]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HaFormMultiSelectSchema extends HaFormBaseSchema {
|
||||||
|
type: "multi_select";
|
||||||
|
options: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HaFormFloatSchema extends HaFormBaseSchema {
|
||||||
|
type: "float";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HaFormStringSchema extends HaFormBaseSchema {
|
||||||
|
type: "string";
|
||||||
|
format?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HaFormBooleanSchema extends HaFormBaseSchema {
|
||||||
|
type: "boolean";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HaFormTimeSchema extends HaFormBaseSchema {
|
||||||
|
type: "positive_time_period_dict";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HaFormDataContainer {
|
||||||
|
[key: string]: HaFormData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type HaFormData =
|
||||||
|
| HaFormStringData
|
||||||
|
| HaFormIntegerData
|
||||||
|
| HaFormFloatData
|
||||||
|
| HaFormBooleanData
|
||||||
|
| HaFormSelectData
|
||||||
|
| HaFormMultiSelectData
|
||||||
|
| HaFormTimeData;
|
||||||
|
|
||||||
|
export type HaFormStringData = string;
|
||||||
|
export type HaFormIntegerData = number;
|
||||||
|
export type HaFormFloatData = number;
|
||||||
|
export type HaFormBooleanData = boolean;
|
||||||
|
export type HaFormSelectData = string;
|
||||||
|
export type HaFormMultiSelectData = string[];
|
||||||
|
export type HaFormTimeData = HaDurationData;
|
||||||
|
|
||||||
|
export interface HaFormElement extends LitElement {
|
||||||
|
schema: HaFormSchema | HaFormSchema[];
|
||||||
|
data?: HaFormDataContainer | HaFormData;
|
||||||
|
label?: string;
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import { Connection } from "home-assistant-js-websocket";
|
import { Connection } from "home-assistant-js-websocket";
|
||||||
import { HaFormSchema } from "../components/ha-form/ha-form";
|
import type { HaFormSchema } from "../components/ha-form/types";
|
||||||
import { ConfigEntry } from "./config_entries";
|
import { ConfigEntry } from "./config_entries";
|
||||||
|
|
||||||
export interface DataEntryFlowProgressedEvent {
|
export interface DataEntryFlowProgressedEvent {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { computeStateName } from "../common/entity/compute_state_name";
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
import { HaFormSchema } from "../components/ha-form/ha-form";
|
import type { HaFormSchema } from "../components/ha-form/types";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { BaseTrigger } from "./automation";
|
import { BaseTrigger } from "./automation";
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { atLeastVersion } from "../../common/config/version";
|
import { atLeastVersion } from "../../common/config/version";
|
||||||
import { HaFormSchema } from "../../components/ha-form/ha-form";
|
import type { HaFormSchema } from "../../components/ha-form/types";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { SupervisorArch } from "../supervisor/supervisor";
|
import { SupervisorArch } from "../supervisor/supervisor";
|
||||||
import {
|
import {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { HaFormSchema } from "../components/ha-form/ha-form";
|
import type { HaFormSchema } from "../components/ha-form/types";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
export interface ZHAEntityReference extends HassEntity {
|
export interface ZHAEntityReference extends HassEntity {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { TemplateResult } from "lit";
|
import { TemplateResult } from "lit";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { HaFormSchema } from "../../components/ha-form/ha-form";
|
import type { HaFormSchema } from "../../components/ha-form/types";
|
||||||
import {
|
import {
|
||||||
DataEntryFlowStep,
|
DataEntryFlowStep,
|
||||||
DataEntryFlowStepAbort,
|
DataEntryFlowStepAbort,
|
||||||
|
@ -11,9 +11,11 @@ import {
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import "../../components/ha-circular-progress";
|
import "../../components/ha-circular-progress";
|
||||||
|
import { computeInitialHaFormData } from "../../components/ha-form/compute-initial-ha-form-data";
|
||||||
|
import type { HaFormSchema } from "../../components/ha-form/types";
|
||||||
import "../../components/ha-form/ha-form";
|
import "../../components/ha-form/ha-form";
|
||||||
import type { HaFormSchema } from "../../components/ha-form/ha-form";
|
|
||||||
import "../../components/ha-markdown";
|
import "../../components/ha-markdown";
|
||||||
|
import "../../components/ha-alert";
|
||||||
import type { DataEntryFlowStepForm } from "../../data/data_entry_flow";
|
import type { DataEntryFlowStepForm } from "../../data/data_entry_flow";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import type { FlowConfig } from "./show-dialog-data-entry-flow";
|
import type { FlowConfig } from "./show-dialog-data-entry-flow";
|
||||||
@ -37,24 +39,13 @@ class StepFlowForm extends LitElement {
|
|||||||
const step = this.step;
|
const step = this.step;
|
||||||
const stepData = this._stepDataProcessed;
|
const stepData = this._stepDataProcessed;
|
||||||
|
|
||||||
const allRequiredInfoFilledIn =
|
|
||||||
stepData === undefined
|
|
||||||
? // If no data filled in, just check that any field is required
|
|
||||||
step.data_schema.find((field) => !field.optional) === undefined
|
|
||||||
: // If data is filled in, make sure all required fields are
|
|
||||||
stepData &&
|
|
||||||
step.data_schema.every(
|
|
||||||
(field) =>
|
|
||||||
field.optional || !["", undefined].includes(stepData![field.name])
|
|
||||||
);
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<h2>${this.flowConfig.renderShowFormStepHeader(this.hass, this.step)}</h2>
|
<h2>${this.flowConfig.renderShowFormStepHeader(this.hass, this.step)}</h2>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
${this._errorMsg
|
|
||||||
? html` <div class="error">${this._errorMsg}</div> `
|
|
||||||
: ""}
|
|
||||||
${this.flowConfig.renderShowFormStepDescription(this.hass, this.step)}
|
${this.flowConfig.renderShowFormStepDescription(this.hass, this.step)}
|
||||||
|
${this._errorMsg
|
||||||
|
? html`<ha-alert alert-type="error">${this._errorMsg}</ha-alert>`
|
||||||
|
: ""}
|
||||||
<ha-form
|
<ha-form
|
||||||
.data=${stepData}
|
.data=${stepData}
|
||||||
@value-changed=${this._stepDataChanged}
|
@value-changed=${this._stepDataChanged}
|
||||||
@ -73,25 +64,13 @@ class StepFlowForm extends LitElement {
|
|||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<div>
|
<div>
|
||||||
<mwc-button
|
<mwc-button @click=${this._submitStep}>
|
||||||
@click=${this._submitStep}
|
${this.hass.localize(
|
||||||
.disabled=${!allRequiredInfoFilledIn}
|
|
||||||
>${this.hass.localize(
|
|
||||||
`ui.panel.config.integrations.config_flow.${
|
`ui.panel.config.integrations.config_flow.${
|
||||||
this.step.last_step === false ? "next" : "submit"
|
this.step.last_step === false ? "next" : "submit"
|
||||||
}`
|
}`
|
||||||
)}
|
)}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
|
|
||||||
${!allRequiredInfoFilledIn
|
|
||||||
? html`
|
|
||||||
<paper-tooltip animation-delay="0" position="left"
|
|
||||||
>${this.hass.localize(
|
|
||||||
"ui.panel.config.integrations.config_flow.not_all_required_fields"
|
|
||||||
)}
|
|
||||||
</paper-tooltip>
|
|
||||||
`
|
|
||||||
: html``}
|
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
</div>
|
</div>
|
||||||
@ -113,25 +92,35 @@ class StepFlowForm extends LitElement {
|
|||||||
return this._stepData;
|
return this._stepData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = {};
|
this._stepData = computeInitialHaFormData(this.step.data_schema);
|
||||||
this.step.data_schema.forEach((field) => {
|
return this._stepData;
|
||||||
if (field.description?.suggested_value) {
|
|
||||||
data[field.name] = field.description.suggested_value;
|
|
||||||
} else if ("default" in field) {
|
|
||||||
data[field.name] = field.default;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this._stepData = data;
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _submitStep(): Promise<void> {
|
private async _submitStep(): Promise<void> {
|
||||||
|
const stepData = this._stepData || {};
|
||||||
|
|
||||||
|
const allRequiredInfoFilledIn =
|
||||||
|
stepData === undefined
|
||||||
|
? // If no data filled in, just check that any field is required
|
||||||
|
this.step.data_schema.find((field) => !field.optional) === undefined
|
||||||
|
: // If data is filled in, make sure all required fields are
|
||||||
|
stepData &&
|
||||||
|
this.step.data_schema.every(
|
||||||
|
(field) =>
|
||||||
|
field.optional || !["", undefined].includes(stepData![field.name])
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!allRequiredInfoFilledIn) {
|
||||||
|
this._errorMsg = this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_flow.not_all_required_fields"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._loading = true;
|
this._loading = true;
|
||||||
this._errorMsg = undefined;
|
this._errorMsg = undefined;
|
||||||
|
|
||||||
const flowId = this.step.flow_id;
|
const flowId = this.step.flow_id;
|
||||||
const stepData = this._stepData || {};
|
|
||||||
|
|
||||||
const toSendData = {};
|
const toSendData = {};
|
||||||
Object.keys(stepData).forEach((key) => {
|
Object.keys(stepData).forEach((key) => {
|
||||||
@ -188,6 +177,12 @@ class StepFlowForm extends LitElement {
|
|||||||
.submit-spinner {
|
.submit-spinner {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ha-alert,
|
||||||
|
ha-form {
|
||||||
|
margin-top: 24px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,8 @@ export const configFlowContentStyles = css`
|
|||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 8px 8px 8px 24px;
|
padding: 8px 16px 8px 24px;
|
||||||
margin: 0;
|
margin: 8px 0 0;
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
@ -59,7 +59,7 @@ documentContainer.innerHTML = `<custom-style>
|
|||||||
|
|
||||||
/* states */
|
/* states */
|
||||||
--state-icon-color: #44739e;
|
--state-icon-color: #44739e;
|
||||||
/* an active state is anything that would require attention */
|
/* an active state is anything that would require attention */
|
||||||
--state-icon-active-color: #FDD835;
|
--state-icon-active-color: #FDD835;
|
||||||
/* an error state is anything that would be considered an error */
|
/* an error state is anything that would be considered an error */
|
||||||
/* --state-icon-error-color: #db4437; derived from error-color */
|
/* --state-icon-error-color: #db4437; derived from error-color */
|
||||||
@ -112,6 +112,20 @@ documentContainer.innerHTML = `<custom-style>
|
|||||||
--rgb-text-primary-color: 255, 255, 255;
|
--rgb-text-primary-color: 255, 255, 255;
|
||||||
--rgb-card-background-color: 255, 255, 255;
|
--rgb-card-background-color: 255, 255, 255;
|
||||||
|
|
||||||
|
/* input components */
|
||||||
|
--input-idle-line-color: rgba(0, 0, 0, 0.42);
|
||||||
|
--input-hover-line-color: rgba(0, 0, 0, 0.87);
|
||||||
|
--input-disabled-line-color: rgba(0, 0, 0, 0.06);
|
||||||
|
--input-outlined-idle-border-color: rgba(0, 0, 0, 0.38);
|
||||||
|
--input-outlined-hover-border-color: rgba(0, 0, 0, 0.87);
|
||||||
|
--input-outlined-disabled-border-color: rgba(0, 0, 0, 0.06);
|
||||||
|
--input-fill-color: rgb(245, 245, 245);
|
||||||
|
--input-disabled-fill-color: rgb(250, 250, 250);
|
||||||
|
--input-ink-color: rgba(0, 0, 0, 0.87);
|
||||||
|
--input-label-ink-color: rgba(0, 0, 0, 0.6);
|
||||||
|
--input-disabled-ink-color: rgba(0, 0, 0, 0.37);
|
||||||
|
--input-dropdown-icon-color: rgba(0, 0, 0, 0.54);
|
||||||
|
|
||||||
/* Vaadin typography */
|
/* Vaadin typography */
|
||||||
--material-h6-font-size: 1.25rem;
|
--material-h6-font-size: 1.25rem;
|
||||||
--material-small-font-size: 0.875rem;
|
--material-small-font-size: 0.875rem;
|
||||||
|
@ -13,6 +13,20 @@ export const darkStyles = {
|
|||||||
"switch-unchecked-track-color": "#9b9b9b",
|
"switch-unchecked-track-color": "#9b9b9b",
|
||||||
"divider-color": "rgba(225, 225, 225, .12)",
|
"divider-color": "rgba(225, 225, 225, .12)",
|
||||||
"mdc-ripple-color": "#AAAAAA",
|
"mdc-ripple-color": "#AAAAAA",
|
||||||
|
|
||||||
|
"input-idle-line-color": "rgba(255, 255, 255, 0.42)",
|
||||||
|
"input-hover-line-color": "rgba(255, 255, 255, 0.87)",
|
||||||
|
"input-disabled-line-color": "rgba(255, 255, 255, 0.06)",
|
||||||
|
"input-outlined-idle-border-color": "rgba(255, 255, 255, 0.38)",
|
||||||
|
"input-outlined-hover-border-color": "rgba(255, 255, 255, 0.87)",
|
||||||
|
"input-outlined-disabled-border-color": "rgba(255, 255, 255, 0.06)",
|
||||||
|
"input-fill-color": "rgb(10, 10, 10)",
|
||||||
|
"input-disabled-fill-color": "rgb(5, 5, 5)",
|
||||||
|
"input-ink-color": "rgba(255, 255, 255, 0.87)",
|
||||||
|
"input-label-ink-color": "rgba(255, 255, 255, 0.6)",
|
||||||
|
"input-disabled-ink-color": "rgba(255, 255, 255, 0.37)",
|
||||||
|
"input-dropdown-icon-color": "rgba(255, 255, 255, 0.54)",
|
||||||
|
|
||||||
"codemirror-keyword": "#C792EA",
|
"codemirror-keyword": "#C792EA",
|
||||||
"codemirror-operator": "#89DDFF",
|
"codemirror-operator": "#89DDFF",
|
||||||
"codemirror-variable": "#f07178",
|
"codemirror-variable": "#f07178",
|
||||||
@ -69,6 +83,8 @@ export const derivedStyles = {
|
|||||||
"paper-slider-container-color": "var(--slider-track-color)",
|
"paper-slider-container-color": "var(--slider-track-color)",
|
||||||
"data-table-background-color": "var(--card-background-color)",
|
"data-table-background-color": "var(--card-background-color)",
|
||||||
"markdown-code-background-color": "var(--primary-background-color)",
|
"markdown-code-background-color": "var(--primary-background-color)",
|
||||||
|
|
||||||
|
// https://github.com/material-components/material-web/blob/master/docs/theming.md
|
||||||
"mdc-theme-primary": "var(--primary-color)",
|
"mdc-theme-primary": "var(--primary-color)",
|
||||||
"mdc-theme-secondary": "var(--accent-color)",
|
"mdc-theme-secondary": "var(--accent-color)",
|
||||||
"mdc-theme-background": "var(--primary-background-color)",
|
"mdc-theme-background": "var(--primary-background-color)",
|
||||||
@ -80,6 +96,7 @@ export const derivedStyles = {
|
|||||||
"mdc-theme-text-primary-on-background": "var(--primary-text-color)",
|
"mdc-theme-text-primary-on-background": "var(--primary-text-color)",
|
||||||
"mdc-theme-text-secondary-on-background": "var(--secondary-text-color)",
|
"mdc-theme-text-secondary-on-background": "var(--secondary-text-color)",
|
||||||
"mdc-theme-text-icon-on-background": "var(--secondary-text-color)",
|
"mdc-theme-text-icon-on-background": "var(--secondary-text-color)",
|
||||||
|
"mdc-theme-error": "var(--error-color)",
|
||||||
"app-header-text-color": "var(--text-primary-color)",
|
"app-header-text-color": "var(--text-primary-color)",
|
||||||
"app-header-background-color": "var(--primary-color)",
|
"app-header-background-color": "var(--primary-color)",
|
||||||
"mdc-checkbox-unchecked-color": "rgba(var(--rgb-primary-text-color), 0.54)",
|
"mdc-checkbox-unchecked-color": "rgba(var(--rgb-primary-text-color), 0.54)",
|
||||||
@ -90,6 +107,38 @@ export const derivedStyles = {
|
|||||||
"mdc-button-disabled-ink-color": "var(--disabled-text-color)",
|
"mdc-button-disabled-ink-color": "var(--disabled-text-color)",
|
||||||
"mdc-button-outline-color": "var(--divider-color)",
|
"mdc-button-outline-color": "var(--divider-color)",
|
||||||
"mdc-dialog-scroll-divider-color": "var(--divider-color)",
|
"mdc-dialog-scroll-divider-color": "var(--divider-color)",
|
||||||
|
|
||||||
|
"mdc-text-field-idle-line-color": "var(--input-idle-line-color)",
|
||||||
|
"mdc-text-field-hover-line-color": "var(--input-hover-line-color)",
|
||||||
|
"mdc-text-field-disabled-line-color": "var(--input-disabled-line-color)",
|
||||||
|
"mdc-text-field-outlined-idle-border-color":
|
||||||
|
"var(--input-outlined-idle-border-color)",
|
||||||
|
"mdc-text-field-outlined-hover-border-color":
|
||||||
|
"var(--input-outlined-hover-border-color)",
|
||||||
|
"mdc-text-field-outlined-disabled-border-color":
|
||||||
|
"var(--input-outlined-disabled-border-color)",
|
||||||
|
"mdc-text-field-fill-color": "var(--input-fill-color)",
|
||||||
|
"mdc-text-field-disabled-fill-color": "var(--input-disabled-fill-color)",
|
||||||
|
"mdc-text-field-ink-color": "var(--input-ink-color)",
|
||||||
|
"mdc-text-field-label-ink-color": "var(--input-label-ink-color)",
|
||||||
|
"mdc-text-field-disabled-ink-color": "var(--input-disabled-ink-color)",
|
||||||
|
|
||||||
|
"mdc-select-idle-line-color": "var(--input-idle-line-color)",
|
||||||
|
"mdc-select-hover-line-color": "var(--input-hover-line-color)",
|
||||||
|
"mdc-select-outlined-idle-border-color":
|
||||||
|
"var(--input-outlined-idle-border-color)",
|
||||||
|
"mdc-select-outlined-hover-border-color":
|
||||||
|
"var(--input-outlined-hover-border-color)",
|
||||||
|
"mdc-select-outlined-disabled-border-color":
|
||||||
|
"var(--input-outlined-disabled-border-color)",
|
||||||
|
"mdc-select-fill-color": "var(--input-fill-color)",
|
||||||
|
"mdc-select-disabled-fill-color": "var(--input-disabled-fill-color)",
|
||||||
|
"mdc-select-ink-color": "var(--input-ink-color)",
|
||||||
|
"mdc-select-label-ink-color": "var(--input-label-ink-color)",
|
||||||
|
"mdc-select-disabled-ink-color": "var(--input-disabled-ink-color)",
|
||||||
|
"mdc-select-dropdown-icon-color": "var(--input-dropdown-icon-color)",
|
||||||
|
"mdc-select-disabled-dropdown-icon-color": "var(--input-disabled-ink-color)",
|
||||||
|
|
||||||
"chip-background-color": "rgba(var(--rgb-primary-text-color), 0.15)",
|
"chip-background-color": "rgba(var(--rgb-primary-text-color), 0.15)",
|
||||||
// Vaadin
|
// Vaadin
|
||||||
"material-body-text-color": "var(--primary-text-color)",
|
"material-body-text-color": "var(--primary-text-color)",
|
||||||
|
@ -3520,7 +3520,7 @@
|
|||||||
"form": {
|
"form": {
|
||||||
"working": "Please wait",
|
"working": "Please wait",
|
||||||
"unknown_error": "Something went wrong",
|
"unknown_error": "Something went wrong",
|
||||||
"next": "Next",
|
"next": "Login",
|
||||||
"start_over": "Start over",
|
"start_over": "Start over",
|
||||||
"error": "Error: {error}",
|
"error": "Error: {error}",
|
||||||
"providers": {
|
"providers": {
|
||||||
|
146
yarn.lock
146
yarn.lock
@ -2149,7 +2149,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@material/floating-label@npm:13.0.0-canary.65125b3a6.0":
|
"@material/floating-label@npm:13.0.0-canary.65125b3a6.0, @material/floating-label@npm:=13.0.0-canary.65125b3a6.0":
|
||||||
version: 13.0.0-canary.65125b3a6.0
|
version: 13.0.0-canary.65125b3a6.0
|
||||||
resolution: "@material/floating-label@npm:13.0.0-canary.65125b3a6.0"
|
resolution: "@material/floating-label@npm:13.0.0-canary.65125b3a6.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2197,7 +2197,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@material/line-ripple@npm:13.0.0-canary.65125b3a6.0":
|
"@material/line-ripple@npm:13.0.0-canary.65125b3a6.0, @material/line-ripple@npm:=13.0.0-canary.65125b3a6.0":
|
||||||
version: 13.0.0-canary.65125b3a6.0
|
version: 13.0.0-canary.65125b3a6.0
|
||||||
resolution: "@material/line-ripple@npm:13.0.0-canary.65125b3a6.0"
|
resolution: "@material/line-ripple@npm:13.0.0-canary.65125b3a6.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2359,6 +2359,18 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@material/mwc-floating-label@npm:^0.25.1":
|
||||||
|
version: 0.25.1
|
||||||
|
resolution: "@material/mwc-floating-label@npm:0.25.1"
|
||||||
|
dependencies:
|
||||||
|
"@material/floating-label": =13.0.0-canary.65125b3a6.0
|
||||||
|
lit-element: ^3.0.0
|
||||||
|
lit-html: ^2.0.0
|
||||||
|
tslib: ^2.0.1
|
||||||
|
checksum: 22d91998c1d01115e8ac59b71b5fe35cb8dc7c781bedb191377659536c0f190d99d5965cab41e67fe3bfc5100fdcdb0659c3f52b3712e89e4ae3994f5dc8319e
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@material/mwc-formfield@npm:0.25.1":
|
"@material/mwc-formfield@npm:0.25.1":
|
||||||
version: 0.25.1
|
version: 0.25.1
|
||||||
resolution: "@material/mwc-formfield@npm:0.25.1"
|
resolution: "@material/mwc-formfield@npm:0.25.1"
|
||||||
@ -2393,6 +2405,18 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@material/mwc-line-ripple@npm:^0.25.1":
|
||||||
|
version: 0.25.1
|
||||||
|
resolution: "@material/mwc-line-ripple@npm:0.25.1"
|
||||||
|
dependencies:
|
||||||
|
"@material/line-ripple": =13.0.0-canary.65125b3a6.0
|
||||||
|
lit-element: ^3.0.0
|
||||||
|
lit-html: ^2.0.0
|
||||||
|
tslib: ^2.0.1
|
||||||
|
checksum: 5897f55cd11e134a2adea03b4cffaa46bf5d8fb71dff2c0601a53960f4ff35a4ba82ea443bdbe72daca801d3abc1b7a459bd980dbeab59dbd7b216b5e10fc1f2
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@material/mwc-linear-progress@npm:0.25.1":
|
"@material/mwc-linear-progress@npm:0.25.1":
|
||||||
version: 0.25.1
|
version: 0.25.1
|
||||||
resolution: "@material/mwc-linear-progress@npm:0.25.1"
|
resolution: "@material/mwc-linear-progress@npm:0.25.1"
|
||||||
@ -2425,7 +2449,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@material/mwc-menu@npm:0.25.1":
|
"@material/mwc-menu@npm:0.25.1, @material/mwc-menu@npm:^0.25.1":
|
||||||
version: 0.25.1
|
version: 0.25.1
|
||||||
resolution: "@material/mwc-menu@npm:0.25.1"
|
resolution: "@material/mwc-menu@npm:0.25.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2442,6 +2466,19 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@material/mwc-notched-outline@npm:^0.25.1":
|
||||||
|
version: 0.25.1
|
||||||
|
resolution: "@material/mwc-notched-outline@npm:0.25.1"
|
||||||
|
dependencies:
|
||||||
|
"@material/mwc-base": ^0.25.1
|
||||||
|
"@material/notched-outline": =13.0.0-canary.65125b3a6.0
|
||||||
|
lit-element: ^3.0.0
|
||||||
|
lit-html: ^2.0.0
|
||||||
|
tslib: ^2.0.1
|
||||||
|
checksum: e86709d2f0b6b118a8ba606055c1e8a633919a834e05b4bf897d472206a6031e9ec20b20cdcccbc1d364939ea948e60aea4132eb17c4225938cc059ab370f301
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@material/mwc-radio@npm:0.25.1, @material/mwc-radio@npm:^0.25.1":
|
"@material/mwc-radio@npm:0.25.1, @material/mwc-radio@npm:^0.25.1":
|
||||||
version: 0.25.1
|
version: 0.25.1
|
||||||
resolution: "@material/mwc-radio@npm:0.25.1"
|
resolution: "@material/mwc-radio@npm:0.25.1"
|
||||||
@ -2469,6 +2506,44 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@material/mwc-select@npm:^0.25.1":
|
||||||
|
version: 0.25.1
|
||||||
|
resolution: "@material/mwc-select@npm:0.25.1"
|
||||||
|
dependencies:
|
||||||
|
"@material/dom": =13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/floating-label": =13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/line-ripple": =13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/list": =13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/mwc-base": ^0.25.1
|
||||||
|
"@material/mwc-floating-label": ^0.25.1
|
||||||
|
"@material/mwc-icon": ^0.25.1
|
||||||
|
"@material/mwc-line-ripple": ^0.25.1
|
||||||
|
"@material/mwc-list": ^0.25.1
|
||||||
|
"@material/mwc-menu": ^0.25.1
|
||||||
|
"@material/mwc-notched-outline": ^0.25.1
|
||||||
|
"@material/select": =13.0.0-canary.65125b3a6.0
|
||||||
|
lit-element: ^3.0.0
|
||||||
|
lit-html: ^2.0.0
|
||||||
|
tslib: ^2.0.1
|
||||||
|
checksum: 542c24e93e16d0a9f101765679c14e823a0c6fd3932b6f33e6a0bd440e436265c2571505db850be3dcff09e0a05d79ffecdc55e0e1340ba8722e181ed3667529
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@material/mwc-slider@npm:^0.25.1":
|
||||||
|
version: 0.25.1
|
||||||
|
resolution: "@material/mwc-slider@npm:0.25.1"
|
||||||
|
dependencies:
|
||||||
|
"@material/dom": =13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/mwc-base": ^0.25.1
|
||||||
|
"@material/mwc-ripple": ^0.25.1
|
||||||
|
"@material/slider": =13.0.0-canary.65125b3a6.0
|
||||||
|
lit-element: ^3.0.0
|
||||||
|
lit-html: ^2.0.0
|
||||||
|
tslib: ^2.0.1
|
||||||
|
checksum: 964ba94e12b2aee8e67b19e0d4fc7b8a8a4945be8bef82aa3a305247b2e318709d1d3ffd9761f87a920f85538a863aca59c2746f8bd85e82125c1d15f98c8768
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@material/mwc-switch@npm:0.25.1":
|
"@material/mwc-switch@npm:0.25.1":
|
||||||
version: 0.25.1
|
version: 0.25.1
|
||||||
resolution: "@material/mwc-switch@npm:0.25.1"
|
resolution: "@material/mwc-switch@npm:0.25.1"
|
||||||
@ -2538,7 +2613,25 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@material/notched-outline@npm:13.0.0-canary.65125b3a6.0":
|
"@material/mwc-textfield@npm:^0.25.1":
|
||||||
|
version: 0.25.1
|
||||||
|
resolution: "@material/mwc-textfield@npm:0.25.1"
|
||||||
|
dependencies:
|
||||||
|
"@material/floating-label": =13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/line-ripple": =13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/mwc-base": ^0.25.1
|
||||||
|
"@material/mwc-floating-label": ^0.25.1
|
||||||
|
"@material/mwc-line-ripple": ^0.25.1
|
||||||
|
"@material/mwc-notched-outline": ^0.25.1
|
||||||
|
"@material/textfield": =13.0.0-canary.65125b3a6.0
|
||||||
|
lit-element: ^3.0.0
|
||||||
|
lit-html: ^2.0.0
|
||||||
|
tslib: ^2.0.1
|
||||||
|
checksum: 31a0235c4b50dcbff28d913c90be114b2972edceb753b17eb84f47cdb46459716a70ee939e9853b8e9ce86af0105e48fea31700e8bac5c30dfadba0f1d5b2235
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@material/notched-outline@npm:13.0.0-canary.65125b3a6.0, @material/notched-outline@npm:=13.0.0-canary.65125b3a6.0":
|
||||||
version: 13.0.0-canary.65125b3a6.0
|
version: 13.0.0-canary.65125b3a6.0
|
||||||
resolution: "@material/notched-outline@npm:13.0.0-canary.65125b3a6.0"
|
resolution: "@material/notched-outline@npm:13.0.0-canary.65125b3a6.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2604,7 +2697,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@material/select@npm:13.0.0-canary.65125b3a6.0":
|
"@material/select@npm:13.0.0-canary.65125b3a6.0, @material/select@npm:=13.0.0-canary.65125b3a6.0":
|
||||||
version: 13.0.0-canary.65125b3a6.0
|
version: 13.0.0-canary.65125b3a6.0
|
||||||
resolution: "@material/select@npm:13.0.0-canary.65125b3a6.0"
|
resolution: "@material/select@npm:13.0.0-canary.65125b3a6.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2641,6 +2734,24 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@material/slider@npm:=13.0.0-canary.65125b3a6.0":
|
||||||
|
version: 13.0.0-canary.65125b3a6.0
|
||||||
|
resolution: "@material/slider@npm:13.0.0-canary.65125b3a6.0"
|
||||||
|
dependencies:
|
||||||
|
"@material/animation": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/base": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/dom": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/elevation": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/feature-targeting": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/ripple": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/rtl": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/theme": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/typography": 13.0.0-canary.65125b3a6.0
|
||||||
|
tslib: ^2.1.0
|
||||||
|
checksum: 26f6de62b296296b196cfa31193e137f91da8a4eb6c1e8c7209a73eac3c2d4bbbb412e020b715942ab861632701a6c9931e8b845a4f7cc92b6700f43b811c50c
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@material/switch@npm:=13.0.0-canary.65125b3a6.0":
|
"@material/switch@npm:=13.0.0-canary.65125b3a6.0":
|
||||||
version: 13.0.0-canary.65125b3a6.0
|
version: 13.0.0-canary.65125b3a6.0
|
||||||
resolution: "@material/switch@npm:13.0.0-canary.65125b3a6.0"
|
resolution: "@material/switch@npm:13.0.0-canary.65125b3a6.0"
|
||||||
@ -2724,6 +2835,28 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@material/textfield@npm:=13.0.0-canary.65125b3a6.0":
|
||||||
|
version: 13.0.0-canary.65125b3a6.0
|
||||||
|
resolution: "@material/textfield@npm:13.0.0-canary.65125b3a6.0"
|
||||||
|
dependencies:
|
||||||
|
"@material/animation": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/base": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/density": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/dom": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/feature-targeting": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/floating-label": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/line-ripple": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/notched-outline": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/ripple": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/rtl": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/shape": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/theme": 13.0.0-canary.65125b3a6.0
|
||||||
|
"@material/typography": 13.0.0-canary.65125b3a6.0
|
||||||
|
tslib: ^2.1.0
|
||||||
|
checksum: 1be6d8c1941729a580cb56fd9079049e9cb0dc9c648708af19683a11bd2f14b21b212cc8cf41f77c8cac9441438fe1f17696b70264daf8e0ff72f22baec7136a
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@material/theme@npm:13.0.0-canary.65125b3a6.0, @material/theme@npm:=13.0.0-canary.65125b3a6.0":
|
"@material/theme@npm:13.0.0-canary.65125b3a6.0, @material/theme@npm:=13.0.0-canary.65125b3a6.0":
|
||||||
version: 13.0.0-canary.65125b3a6.0
|
version: 13.0.0-canary.65125b3a6.0
|
||||||
resolution: "@material/theme@npm:13.0.0-canary.65125b3a6.0"
|
resolution: "@material/theme@npm:13.0.0-canary.65125b3a6.0"
|
||||||
@ -8978,9 +9111,12 @@ fsevents@^1.2.7:
|
|||||||
"@material/mwc-menu": 0.25.1
|
"@material/mwc-menu": 0.25.1
|
||||||
"@material/mwc-radio": 0.25.1
|
"@material/mwc-radio": 0.25.1
|
||||||
"@material/mwc-ripple": 0.25.1
|
"@material/mwc-ripple": 0.25.1
|
||||||
|
"@material/mwc-select": ^0.25.1
|
||||||
|
"@material/mwc-slider": ^0.25.1
|
||||||
"@material/mwc-switch": 0.25.1
|
"@material/mwc-switch": 0.25.1
|
||||||
"@material/mwc-tab": 0.25.1
|
"@material/mwc-tab": 0.25.1
|
||||||
"@material/mwc-tab-bar": 0.25.1
|
"@material/mwc-tab-bar": 0.25.1
|
||||||
|
"@material/mwc-textfield": ^0.25.1
|
||||||
"@material/top-app-bar": 13.0.0-canary.65125b3a6.0
|
"@material/top-app-bar": 13.0.0-canary.65125b3a6.0
|
||||||
"@mdi/js": 6.2.95
|
"@mdi/js": 6.2.95
|
||||||
"@mdi/svg": 6.2.95
|
"@mdi/svg": 6.2.95
|
||||||
|
Loading…
x
Reference in New Issue
Block a user