mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 17:26:42 +00:00
Merge pull request #11969 from home-assistant/patch-release
This commit is contained in:
commit
e9003ac35e
@ -38,6 +38,7 @@ const SCHEMAS: {
|
|||||||
select: "Select",
|
select: "Select",
|
||||||
icon: "Icon",
|
icon: "Icon",
|
||||||
media: "Media",
|
media: "Media",
|
||||||
|
location: "Location",
|
||||||
},
|
},
|
||||||
schema: [
|
schema: [
|
||||||
{ name: "addon", selector: { addon: {} } },
|
{ name: "addon", selector: { addon: {} } },
|
||||||
@ -75,6 +76,10 @@ const SCHEMAS: {
|
|||||||
media: {},
|
media: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "location",
|
||||||
|
selector: { location: { radius: true, icon: "mdi:home" } },
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -168,6 +168,11 @@ const SCHEMAS: {
|
|||||||
},
|
},
|
||||||
icon: { name: "Icon", selector: { icon: {} } },
|
icon: { name: "Icon", selector: { icon: {} } },
|
||||||
media: { name: "Media", selector: { media: {} } },
|
media: { name: "Media", selector: { media: {} } },
|
||||||
|
location: { name: "Location", selector: { location: {} } },
|
||||||
|
location_radius: {
|
||||||
|
name: "Location with radius",
|
||||||
|
selector: { location: { radius: true, icon: "mdi:home" } },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { mdiFolderUpload } from "@mdi/js";
|
import { mdiFolderUpload } from "@mdi/js";
|
||||||
import "@polymer/paper-input/paper-input-container";
|
|
||||||
import { html, LitElement, TemplateResult } from "lit";
|
import { html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = home-assistant-frontend
|
name = home-assistant-frontend
|
||||||
version = 20220301.0
|
version = 20220301.1
|
||||||
author = The Home Assistant Authors
|
author = The Home Assistant Authors
|
||||||
author_email = hello@home-assistant.io
|
author_email = hello@home-assistant.io
|
||||||
license = Apache-2.0
|
license = Apache-2.0
|
||||||
|
@ -41,7 +41,7 @@ export class HaDateInput extends LitElement {
|
|||||||
return html`<ha-textfield
|
return html`<ha-textfield
|
||||||
.label=${this.label}
|
.label=${this.label}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
iconTrailing="calendar"
|
iconTrailing
|
||||||
@click=${this._openDialog}
|
@click=${this._openDialog}
|
||||||
.value=${this.value
|
.value=${this.value
|
||||||
? formatDateNumeric(new Date(this.value), this.locale)
|
? formatDateNumeric(new Date(this.value), this.locale)
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
import { mdiChevronDown } from "@mdi/js";
|
import { mdiChevronDown } from "@mdi/js";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import {
|
||||||
import { customElement, property, query } from "lit/decorators";
|
css,
|
||||||
|
CSSResultGroup,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit";
|
||||||
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { nextRender } from "../common/util/render-status";
|
import { nextRender } from "../common/util/render-status";
|
||||||
@ -16,11 +23,21 @@ class HaExpansionPanel extends LitElement {
|
|||||||
|
|
||||||
@property() secondary?: string;
|
@property() secondary?: string;
|
||||||
|
|
||||||
|
@state() _showContent = this.expanded;
|
||||||
|
|
||||||
@query(".container") private _container!: HTMLDivElement;
|
@query(".container") private _container!: HTMLDivElement;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div class="summary" @click=${this._toggleContainer}>
|
<div
|
||||||
|
id="summary"
|
||||||
|
@click=${this._toggleContainer}
|
||||||
|
@keydown=${this._toggleContainer}
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
aria-expanded=${this.expanded}
|
||||||
|
aria-controls="sect1"
|
||||||
|
>
|
||||||
<slot class="header" name="header">
|
<slot class="header" name="header">
|
||||||
${this.header}
|
${this.header}
|
||||||
<slot class="secondary" name="secondary">${this.secondary}</slot>
|
<slot class="secondary" name="secondary">${this.secondary}</slot>
|
||||||
@ -33,21 +50,37 @@ class HaExpansionPanel extends LitElement {
|
|||||||
<div
|
<div
|
||||||
class="container ${classMap({ expanded: this.expanded })}"
|
class="container ${classMap({ expanded: this.expanded })}"
|
||||||
@transitionend=${this._handleTransitionEnd}
|
@transitionend=${this._handleTransitionEnd}
|
||||||
|
role="region"
|
||||||
|
aria-labelledby="summary"
|
||||||
|
aria-hidden=${!this.expanded}
|
||||||
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
<slot></slot>
|
${this._showContent ? html`<slot></slot>` : ""}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleTransitionEnd() {
|
protected willUpdate(changedProps: PropertyValues) {
|
||||||
this._container.style.removeProperty("height");
|
if (changedProps.has("expanded") && this.expanded) {
|
||||||
|
this._showContent = this.expanded;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _toggleContainer(): Promise<void> {
|
private _handleTransitionEnd() {
|
||||||
|
this._container.style.removeProperty("height");
|
||||||
|
this._showContent = this.expanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _toggleContainer(ev): Promise<void> {
|
||||||
|
if (ev.type === "keydown" && ev.key !== "Enter" && ev.key !== " ") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ev.preventDefault();
|
||||||
const newExpanded = !this.expanded;
|
const newExpanded = !this.expanded;
|
||||||
fireEvent(this, "expanded-will-change", { expanded: newExpanded });
|
fireEvent(this, "expanded-will-change", { expanded: newExpanded });
|
||||||
|
|
||||||
if (newExpanded) {
|
if (newExpanded) {
|
||||||
|
this._showContent = true;
|
||||||
// allow for dynamic content to be rendered
|
// allow for dynamic content to be rendered
|
||||||
await nextRender();
|
await nextRender();
|
||||||
}
|
}
|
||||||
@ -80,17 +113,21 @@ class HaExpansionPanel extends LitElement {
|
|||||||
var(--divider-color, #e0e0e0)
|
var(--divider-color, #e0e0e0)
|
||||||
);
|
);
|
||||||
border-radius: var(--ha-card-border-radius, 4px);
|
border-radius: var(--ha-card-border-radius, 4px);
|
||||||
padding: 0 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.summary {
|
#summary {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: var(--expansion-panel-summary-padding, 0);
|
padding: var(--expansion-panel-summary-padding, 0 8px);
|
||||||
min-height: 48px;
|
min-height: 48px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#summary:focus {
|
||||||
|
background: var(--input-fill-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.summary-icon {
|
.summary-icon {
|
||||||
@ -103,6 +140,7 @@ class HaExpansionPanel extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
|
padding: var(--expansion-panel-content-padding, 0 8px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
height: 0px;
|
height: 0px;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
|
import { styles } from "@material/mwc-textfield/mwc-textfield.css";
|
||||||
import { mdiClose } from "@mdi/js";
|
import { mdiClose } from "@mdi/js";
|
||||||
import "@polymer/iron-input/iron-input";
|
|
||||||
import "@polymer/paper-input/paper-input-container";
|
|
||||||
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
@ -21,7 +20,7 @@ export class HaFileUpload extends LitElement {
|
|||||||
|
|
||||||
@property() public accept!: string;
|
@property() public accept!: string;
|
||||||
|
|
||||||
@property() public icon!: string;
|
@property() public icon?: string;
|
||||||
|
|
||||||
@property() public label!: string;
|
@property() public label!: string;
|
||||||
|
|
||||||
@ -39,15 +38,7 @@ export class HaFileUpload extends LitElement {
|
|||||||
protected firstUpdated(changedProperties: PropertyValues) {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
super.firstUpdated(changedProperties);
|
super.firstUpdated(changedProperties);
|
||||||
if (this.autoOpenFileDialog) {
|
if (this.autoOpenFileDialog) {
|
||||||
this._input?.click();
|
this._openFilePicker();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues) {
|
|
||||||
if (changedProperties.has("_drag") && !this.uploading) {
|
|
||||||
(
|
|
||||||
this.shadowRoot!.querySelector("paper-input-container") as any
|
|
||||||
)._setFocused(this._drag);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,31 +51,52 @@ export class HaFileUpload extends LitElement {
|
|||||||
active
|
active
|
||||||
></ha-circular-progress>`
|
></ha-circular-progress>`
|
||||||
: html`
|
: html`
|
||||||
<label for="input">
|
<label
|
||||||
<paper-input-container
|
for="input"
|
||||||
.alwaysFloatLabel=${Boolean(this.value)}
|
class="mdc-text-field mdc-text-field--filled ${classMap({
|
||||||
|
"mdc-text-field--focused": this._drag,
|
||||||
|
"mdc-text-field--with-leading-icon": Boolean(this.icon),
|
||||||
|
"mdc-text-field--with-trailing-icon": Boolean(this.value),
|
||||||
|
})}"
|
||||||
@drop=${this._handleDrop}
|
@drop=${this._handleDrop}
|
||||||
@dragenter=${this._handleDragStart}
|
@dragenter=${this._handleDragStart}
|
||||||
@dragover=${this._handleDragStart}
|
@dragover=${this._handleDragStart}
|
||||||
@dragleave=${this._handleDragEnd}
|
@dragleave=${this._handleDragEnd}
|
||||||
@dragend=${this._handleDragEnd}
|
@dragend=${this._handleDragEnd}
|
||||||
class=${classMap({
|
|
||||||
dragged: this._drag,
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<label for="input" slot="label"> ${this.label} </label>
|
<span class="mdc-text-field__ripple"></span>
|
||||||
<iron-input slot="input">
|
<span
|
||||||
|
class="mdc-floating-label ${this.value || this._drag
|
||||||
|
? "mdc-floating-label--float-above"
|
||||||
|
: ""}"
|
||||||
|
id="label"
|
||||||
|
>${this.label}</span
|
||||||
|
>
|
||||||
|
${this.icon
|
||||||
|
? html`<span
|
||||||
|
class="mdc-text-field__icon mdc-text-field__icon--leading"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<ha-icon-button
|
||||||
|
@click=${this._openFilePicker}
|
||||||
|
.path=${this.icon}
|
||||||
|
></ha-icon-button>
|
||||||
|
</span>`
|
||||||
|
: ""}
|
||||||
|
<div class="value">${this.value}</div>
|
||||||
<input
|
<input
|
||||||
id="input"
|
id="input"
|
||||||
type="file"
|
type="file"
|
||||||
class="file"
|
class="mdc-text-field__input file"
|
||||||
accept=${this.accept}
|
accept=${this.accept}
|
||||||
@change=${this._handleFilePicked}
|
@change=${this._handleFilePicked}
|
||||||
|
aria-labelledby="label"
|
||||||
/>
|
/>
|
||||||
${this.value}
|
|
||||||
</iron-input>
|
|
||||||
${this.value
|
${this.value
|
||||||
? html`
|
? html`<span
|
||||||
|
class="mdc-text-field__icon mdc-text-field__icon--trailing"
|
||||||
|
tabindex="1"
|
||||||
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="suffix"
|
slot="suffix"
|
||||||
@click=${this._clearValue}
|
@click=${this._clearValue}
|
||||||
@ -92,19 +104,22 @@ export class HaFileUpload extends LitElement {
|
|||||||
"close"}
|
"close"}
|
||||||
.path=${mdiClose}
|
.path=${mdiClose}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`
|
</span>`
|
||||||
: html`
|
: ""}
|
||||||
<ha-icon-button
|
<span
|
||||||
slot="suffix"
|
class="mdc-line-ripple ${this._drag
|
||||||
.path=${this.icon}
|
? "mdc-line-ripple--active"
|
||||||
></ha-icon-button>
|
: ""}"
|
||||||
`}
|
></span>
|
||||||
</paper-input-container>
|
|
||||||
</label>
|
</label>
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _openFilePicker() {
|
||||||
|
this._input?.click();
|
||||||
|
}
|
||||||
|
|
||||||
private _handleDrop(ev: DragEvent) {
|
private _handleDrop(ev: DragEvent) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
@ -137,13 +152,35 @@ export class HaFileUpload extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [
|
||||||
paper-input-container {
|
styles,
|
||||||
position: relative;
|
css`
|
||||||
padding: 8px;
|
:host {
|
||||||
margin: 0 -8px;
|
display: block;
|
||||||
}
|
}
|
||||||
paper-input-container.dragged:before {
|
.mdc-text-field--filled {
|
||||||
|
height: auto;
|
||||||
|
padding-top: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.mdc-text-field--filled.mdc-text-field--with-trailing-icon {
|
||||||
|
padding-top: 28px;
|
||||||
|
}
|
||||||
|
.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__icon {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
.mdc-text-field--filled.mdc-text-field--with-trailing-icon
|
||||||
|
.mdc-text-field__icon {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
.mdc-text-field__icon--leading {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.mdc-text-field--filled .mdc-floating-label--float-above {
|
||||||
|
transform: scale(0.75);
|
||||||
|
top: 8px;
|
||||||
|
}
|
||||||
|
.dragged:before {
|
||||||
position: var(--layout-fit_-_position);
|
position: var(--layout-fit_-_position);
|
||||||
top: var(--layout-fit_-_top);
|
top: var(--layout-fit_-_top);
|
||||||
right: var(--layout-fit_-_right);
|
right: var(--layout-fit_-_right);
|
||||||
@ -155,11 +192,14 @@ export class HaFileUpload extends LitElement {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
.value {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
input.file {
|
input.file {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
img {
|
img {
|
||||||
max-width: 125px;
|
max-width: 100%;
|
||||||
max-height: 125px;
|
max-height: 125px;
|
||||||
}
|
}
|
||||||
ha-icon-button {
|
ha-icon-button {
|
||||||
@ -170,7 +210,8 @@ export class HaFileUpload extends LitElement {
|
|||||||
display: block;
|
display: block;
|
||||||
text-align-last: center;
|
text-align-last: center;
|
||||||
}
|
}
|
||||||
`;
|
`,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,9 @@ export class HaFormConstant extends LitElement implements HaFormElement {
|
|||||||
@property() public label!: string;
|
@property() public label!: string;
|
||||||
|
|
||||||
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
|
||||||
|
? `: ${this.schema.value}`
|
||||||
|
: ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
@ -25,6 +25,8 @@ import { HomeAssistant } from "../../types";
|
|||||||
const getValue = (obj, item) =>
|
const getValue = (obj, item) =>
|
||||||
obj ? (!item.name ? obj : obj[item.name]) : null;
|
obj ? (!item.name ? obj : obj[item.name]) : null;
|
||||||
|
|
||||||
|
const getError = (obj, item) => (obj && item.name ? obj[item.name] : null);
|
||||||
|
|
||||||
let selectorImported = false;
|
let selectorImported = false;
|
||||||
|
|
||||||
@customElement("ha-form")
|
@customElement("ha-form")
|
||||||
@ -84,7 +86,7 @@ export class HaForm extends LitElement implements HaFormElement {
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.schema.map((item) => {
|
${this.schema.map((item) => {
|
||||||
const error = getValue(this.error, item);
|
const error = getError(this.error, item);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${error
|
${error
|
||||||
|
@ -40,7 +40,7 @@ export interface HaFormSelector extends HaFormBaseSchema {
|
|||||||
|
|
||||||
export interface HaFormConstantSchema extends HaFormBaseSchema {
|
export interface HaFormConstantSchema extends HaFormBaseSchema {
|
||||||
type: "constant";
|
type: "constant";
|
||||||
value: string;
|
value?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HaFormIntegerSchema extends HaFormBaseSchema {
|
export interface HaFormIntegerSchema extends HaFormBaseSchema {
|
||||||
|
@ -44,6 +44,9 @@ export class HaSelect extends SelectBase {
|
|||||||
.mdc-select:not(.mdc-select--disabled) .mdc-select__icon {
|
.mdc-select:not(.mdc-select--disabled) .mdc-select__icon {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
.mdc-select__anchor {
|
||||||
|
width: var(--ha-select-min-width, 200px);
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
80
src/components/ha-selector/ha-selector-location.ts
Normal file
80
src/components/ha-selector/ha-selector-location.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { html, LitElement } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import type {
|
||||||
|
LocationSelector,
|
||||||
|
LocationSelectorValue,
|
||||||
|
} from "../../data/selector";
|
||||||
|
import "../../panels/lovelace/components/hui-theme-select-editor";
|
||||||
|
import type { HomeAssistant } from "../../types";
|
||||||
|
import type { MarkerLocation } from "../map/ha-locations-editor";
|
||||||
|
import "../map/ha-locations-editor";
|
||||||
|
|
||||||
|
@customElement("ha-selector-location")
|
||||||
|
export class HaLocationSelector extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public selector!: LocationSelector;
|
||||||
|
|
||||||
|
@property() public value?: LocationSelectorValue;
|
||||||
|
|
||||||
|
@property() public label?: string;
|
||||||
|
|
||||||
|
@property({ type: Boolean, reflect: true }) public disabled = false;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
<ha-locations-editor
|
||||||
|
class="flex"
|
||||||
|
.hass=${this.hass}
|
||||||
|
.locations=${this._location(this.selector, this.value)}
|
||||||
|
@location-updated=${this._locationChanged}
|
||||||
|
@radius-updated=${this._radiusChanged}
|
||||||
|
></ha-locations-editor>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _location = memoizeOne(
|
||||||
|
(
|
||||||
|
selector: LocationSelector,
|
||||||
|
value?: LocationSelectorValue
|
||||||
|
): MarkerLocation[] => {
|
||||||
|
const computedStyles = getComputedStyle(this);
|
||||||
|
const zoneRadiusColor = selector.location.radius
|
||||||
|
? computedStyles.getPropertyValue("--zone-radius-color") ||
|
||||||
|
computedStyles.getPropertyValue("--accent-color")
|
||||||
|
: undefined;
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: "location",
|
||||||
|
latitude: value?.latitude || this.hass.config.latitude,
|
||||||
|
longitude: value?.longitude || this.hass.config.longitude,
|
||||||
|
radius: selector.location.radius ? value?.radius || 1000 : undefined,
|
||||||
|
radius_color: zoneRadiusColor,
|
||||||
|
icon: selector.location.icon,
|
||||||
|
location_editable: true,
|
||||||
|
radius_editable: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
private _locationChanged(ev: CustomEvent) {
|
||||||
|
const [latitude, longitude] = ev.detail.location;
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this.value, latitude, longitude },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _radiusChanged(ev: CustomEvent) {
|
||||||
|
const radius = ev.detail.radius;
|
||||||
|
fireEvent(this, "value-changed", { value: { ...this.value, radius } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-selector-location": HaLocationSelector;
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@ import "./ha-selector-time";
|
|||||||
import "./ha-selector-icon";
|
import "./ha-selector-icon";
|
||||||
import "./ha-selector-media";
|
import "./ha-selector-media";
|
||||||
import "./ha-selector-theme";
|
import "./ha-selector-theme";
|
||||||
|
import "./ha-selector-location";
|
||||||
|
|
||||||
@customElement("ha-selector")
|
@customElement("ha-selector")
|
||||||
export class HaSelector extends LitElement {
|
export class HaSelector extends LitElement {
|
||||||
|
@ -9,6 +9,12 @@ export class HaTextField extends TextFieldBase {
|
|||||||
|
|
||||||
@property({ attribute: "error-message" }) public errorMessage?: string;
|
@property({ attribute: "error-message" }) public errorMessage?: string;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
@property({ type: Boolean }) public icon?: boolean;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
@property({ type: Boolean }) public iconTrailing?: boolean;
|
||||||
|
|
||||||
override updated(changedProperties: PropertyValues) {
|
override updated(changedProperties: PropertyValues) {
|
||||||
super.updated(changedProperties);
|
super.updated(changedProperties);
|
||||||
if (
|
if (
|
||||||
@ -53,6 +59,11 @@ export class HaTextField extends TextFieldBase {
|
|||||||
padding-right: var(--text-field-suffix-padding-right, 0px);
|
padding-right: var(--text-field-suffix-padding-right, 0px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mdc-text-field:not(.mdc-text-field--disabled)
|
||||||
|
.mdc-text-field__affix--suffix {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
.mdc-text-field__icon {
|
.mdc-text-field__icon {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ class SearchInput extends LitElement {
|
|||||||
.autofocus=${this.autofocus}
|
.autofocus=${this.autofocus}
|
||||||
.label=${this.label || "Search"}
|
.label=${this.label || "Search"}
|
||||||
.value=${this.filter || ""}
|
.value=${this.filter || ""}
|
||||||
.icon=${true}
|
icon
|
||||||
.iconTrailing=${this.filter || this.suffix}
|
.iconTrailing=${this.filter || this.suffix}
|
||||||
@input=${this._filterInputChanged}
|
@input=${this._filterInputChanged}
|
||||||
>
|
>
|
||||||
|
@ -29,7 +29,7 @@ export const createImage = async (
|
|||||||
body: fd,
|
body: fd,
|
||||||
});
|
});
|
||||||
if (resp.status === 413) {
|
if (resp.status === 413) {
|
||||||
throw new Error("Uploaded image is too large");
|
throw new Error(`Uploaded image is too large (${file.name})`);
|
||||||
} else if (resp.status !== 200) {
|
} else if (resp.status !== 200) {
|
||||||
throw new Error("Unknown error");
|
throw new Error("Unknown error");
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ export const uploadLocalMedia = async (
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (resp.status === 413) {
|
if (resp.status === 413) {
|
||||||
throw new Error("Uploaded image is too large");
|
throw new Error(`Uploaded file is too large (${file.name})`);
|
||||||
} else if (resp.status !== 200) {
|
} else if (resp.status !== 200) {
|
||||||
throw new Error("Unknown error");
|
throw new Error("Unknown error");
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,8 @@ export type Selector =
|
|||||||
| SelectSelector
|
| SelectSelector
|
||||||
| IconSelector
|
| IconSelector
|
||||||
| MediaSelector
|
| MediaSelector
|
||||||
| ThemeSelector;
|
| ThemeSelector
|
||||||
|
| LocationSelector;
|
||||||
|
|
||||||
export interface EntitySelector {
|
export interface EntitySelector {
|
||||||
entity: {
|
entity: {
|
||||||
@ -164,6 +165,16 @@ export interface MediaSelector {
|
|||||||
media: {};
|
media: {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LocationSelector {
|
||||||
|
location: { radius?: boolean; icon?: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LocationSelectorValue {
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
radius?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MediaSelectorValue {
|
export interface MediaSelectorValue {
|
||||||
entity_id?: string;
|
entity_id?: string;
|
||||||
media_content_id?: string;
|
media_content_id?: string;
|
||||||
|
@ -12,12 +12,12 @@ export interface Zone {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ZoneMutableParams {
|
export interface ZoneMutableParams {
|
||||||
|
name: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
latitude: number;
|
latitude: number;
|
||||||
longitude: number;
|
longitude: number;
|
||||||
name: string;
|
passive?: boolean;
|
||||||
passive: boolean;
|
radius?: number;
|
||||||
radius: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchZones = (hass: HomeAssistant) =>
|
export const fetchZones = (hass: HomeAssistant) =>
|
||||||
|
@ -117,13 +117,17 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
);
|
);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
|
let message = err.message || err.body || "Unknown error";
|
||||||
|
if (typeof message !== "string") {
|
||||||
|
message = JSON.stringify(message);
|
||||||
|
}
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
"ui.panel.config.integrations.config_flow.error"
|
"ui.panel.config.integrations.config_flow.error"
|
||||||
),
|
),
|
||||||
text: `${this.hass.localize(
|
text: `${this.hass.localize(
|
||||||
"ui.panel.config.integrations.config_flow.could_not_load"
|
"ui.panel.config.integrations.config_flow.could_not_load"
|
||||||
)}: ${err.message || err.body}`,
|
)}: ${message}`,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -373,13 +377,20 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
step = await this._params!.flowConfig.createFlow(this.hass, handler);
|
step = await this._params!.flowConfig.createFlow(this.hass, handler);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
|
const message =
|
||||||
|
err?.status_code === 404
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_flow.no_config_flow"
|
||||||
|
)
|
||||||
|
: `${this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_flow.could_not_load"
|
||||||
|
)}: ${err?.body?.message || err?.message}`;
|
||||||
|
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
"ui.panel.config.integrations.config_flow.error"
|
"ui.panel.config.integrations.config_flow.error"
|
||||||
),
|
),
|
||||||
text: `${this.hass.localize(
|
text: message,
|
||||||
"ui.panel.config.integrations.config_flow.could_not_load"
|
|
||||||
)}: ${err.message || err.body}`,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@ -12,6 +13,7 @@ import { fireEvent } from "../../../common/dom/fire_event";
|
|||||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||||
|
import "../../../components/ha-select";
|
||||||
import "../../../components/ha-slider";
|
import "../../../components/ha-slider";
|
||||||
import "../../../components/ha-switch";
|
import "../../../components/ha-switch";
|
||||||
import {
|
import {
|
||||||
@ -19,8 +21,6 @@ import {
|
|||||||
HUMIDIFIER_SUPPORT_MODES,
|
HUMIDIFIER_SUPPORT_MODES,
|
||||||
} from "../../../data/humidifier";
|
} from "../../../data/humidifier";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import "@material/mwc-list/mwc-list";
|
|
||||||
import "@material/mwc-list/mwc-list-item";
|
|
||||||
|
|
||||||
class MoreInfoHumidifier extends LitElement {
|
class MoreInfoHumidifier extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@ -67,8 +67,7 @@ class MoreInfoHumidifier extends LitElement {
|
|||||||
|
|
||||||
${supportModes
|
${supportModes
|
||||||
? html`
|
? html`
|
||||||
<div class="container-modes">
|
<ha-select
|
||||||
<mwc-list
|
|
||||||
.label=${hass.localize("ui.card.humidifier.mode")}
|
.label=${hass.localize("ui.card.humidifier.mode")}
|
||||||
.value=${stateObj.attributes.mode}
|
.value=${stateObj.attributes.mode}
|
||||||
fixedMenuPosition
|
fixedMenuPosition
|
||||||
@ -85,8 +84,7 @@ class MoreInfoHumidifier extends LitElement {
|
|||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</mwc-list>
|
</ha-select>
|
||||||
</div>
|
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
|
@ -86,11 +86,11 @@ export class QuickBar extends LitElement {
|
|||||||
|
|
||||||
@state() private _search = "";
|
@state() private _search = "";
|
||||||
|
|
||||||
@state() private _opened = false;
|
@state() private _open = false;
|
||||||
|
|
||||||
@state() private _commandMode = false;
|
@state() private _commandMode = false;
|
||||||
|
|
||||||
@state() private _done = false;
|
@state() private _opened = false;
|
||||||
|
|
||||||
@state() private _narrow = false;
|
@state() private _narrow = false;
|
||||||
|
|
||||||
@ -109,12 +109,12 @@ export class QuickBar extends LitElement {
|
|||||||
"all and (max-width: 450px), all and (max-height: 500px)"
|
"all and (max-width: 450px), all and (max-height: 500px)"
|
||||||
).matches;
|
).matches;
|
||||||
this._initializeItemsIfNeeded();
|
this._initializeItemsIfNeeded();
|
||||||
this._opened = true;
|
this._open = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeDialog() {
|
public closeDialog() {
|
||||||
|
this._open = false;
|
||||||
this._opened = false;
|
this._opened = false;
|
||||||
this._done = false;
|
|
||||||
this._focusSet = false;
|
this._focusSet = false;
|
||||||
this._filter = "";
|
this._filter = "";
|
||||||
this._search = "";
|
this._search = "";
|
||||||
@ -133,7 +133,7 @@ export class QuickBar extends LitElement {
|
|||||||
);
|
);
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this._opened) {
|
if (!this._open) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +162,7 @@ export class QuickBar extends LitElement {
|
|||||||
"ui.dialogs.quick-bar.filter_placeholder"
|
"ui.dialogs.quick-bar.filter_placeholder"
|
||||||
)}
|
)}
|
||||||
.value=${this._commandMode ? `>${this._search}` : this._search}
|
.value=${this._commandMode ? `>${this._search}` : this._search}
|
||||||
.icon=${true}
|
icon
|
||||||
.iconTrailing=${this._search !== undefined || this._narrow}
|
.iconTrailing=${this._search !== undefined || this._narrow}
|
||||||
@input=${this._handleSearchChange}
|
@input=${this._handleSearchChange}
|
||||||
@keydown=${this._handleInputKeyDown}
|
@keydown=${this._handleInputKeyDown}
|
||||||
@ -218,7 +218,8 @@ export class QuickBar extends LitElement {
|
|||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<mwc-list>
|
<mwc-list>
|
||||||
<lit-virtualizer
|
${this._opened
|
||||||
|
? html`<lit-virtualizer
|
||||||
scroller
|
scroller
|
||||||
@keydown=${this._handleListItemKeyDown}
|
@keydown=${this._handleListItemKeyDown}
|
||||||
@rangechange=${this._handleRangeChanged}
|
@rangechange=${this._handleRangeChanged}
|
||||||
@ -229,13 +230,14 @@ export class QuickBar extends LitElement {
|
|||||||
? "calc(100vh - 56px)"
|
? "calc(100vh - 56px)"
|
||||||
: `${Math.min(
|
: `${Math.min(
|
||||||
items.length * (this._commandMode ? 56 : 72) + 26,
|
items.length * (this._commandMode ? 56 : 72) + 26,
|
||||||
this._done ? 500 : 0
|
500
|
||||||
)}px`,
|
)}px`,
|
||||||
})}
|
})}
|
||||||
.items=${items}
|
.items=${items}
|
||||||
.renderItem=${this._renderItem}
|
.renderItem=${this._renderItem}
|
||||||
>
|
>
|
||||||
</lit-virtualizer>
|
</lit-virtualizer>`
|
||||||
|
: ""}
|
||||||
</mwc-list>
|
</mwc-list>
|
||||||
`}
|
`}
|
||||||
${this._hint ? html`<div class="hint">${this._hint}</div>` : ""}
|
${this._hint ? html`<div class="hint">${this._hint}</div>` : ""}
|
||||||
@ -252,9 +254,7 @@ export class QuickBar extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleOpened() {
|
private _handleOpened() {
|
||||||
this.updateComplete.then(() => {
|
this._opened = true;
|
||||||
this._done = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _handleRangeChanged(e) {
|
private async _handleRangeChanged(e) {
|
||||||
@ -454,9 +454,10 @@ export class QuickBar extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleItemClick(ev) {
|
private _handleItemClick(ev) {
|
||||||
|
const listItem = ev.target.closest("mwc-list-item");
|
||||||
this.processItemAndCloseDialog(
|
this.processItemAndCloseDialog(
|
||||||
(ev.target as any).item,
|
listItem.item,
|
||||||
Number((ev.target as HTMLElement).getAttribute("index"))
|
Number(listItem.getAttribute("index"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
/* eslint-disable lit/prefer-static-styles */
|
/* eslint-disable lit/prefer-static-styles */
|
||||||
|
import "@material/mwc-button/mwc-button";
|
||||||
import { mdiMicrophone } from "@mdi/js";
|
import { mdiMicrophone } from "@mdi/js";
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@ -10,12 +9,16 @@ import {
|
|||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state, query } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { SpeechRecognition } from "../../common/dom/speech-recognition";
|
import { SpeechRecognition } from "../../common/dom/speech-recognition";
|
||||||
import { uid } from "../../common/util/uid";
|
import { uid } from "../../common/util/uid";
|
||||||
|
import "../../components/ha-dialog";
|
||||||
|
import type { HaDialog } from "../../components/ha-dialog";
|
||||||
import "../../components/ha-icon-button";
|
import "../../components/ha-icon-button";
|
||||||
|
import "../../components/ha-textfield";
|
||||||
|
import type { HaTextField } from "../../components/ha-textfield";
|
||||||
import {
|
import {
|
||||||
AgentInfo,
|
AgentInfo,
|
||||||
getAgentInfo,
|
getAgentInfo,
|
||||||
@ -24,9 +27,6 @@ import {
|
|||||||
} from "../../data/conversation";
|
} from "../../data/conversation";
|
||||||
import { haStyleDialog } from "../../resources/styles";
|
import { haStyleDialog } from "../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import "../../components/ha-dialog";
|
|
||||||
import type { HaDialog } from "../../components/ha-dialog";
|
|
||||||
import "@material/mwc-button/mwc-button";
|
|
||||||
|
|
||||||
interface Message {
|
interface Message {
|
||||||
who: string;
|
who: string;
|
||||||
@ -127,18 +127,19 @@ export class HaVoiceCommandDialog extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<div class="input" slot="primaryAction">
|
<div class="input" slot="primaryAction">
|
||||||
<paper-input
|
<ha-textfield
|
||||||
@keyup=${this._handleKeyUp}
|
@keyup=${this._handleKeyUp}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
`ui.dialogs.voice_command.${
|
`ui.dialogs.voice_command.${
|
||||||
SpeechRecognition ? "label_voice" : "label"
|
SpeechRecognition ? "label_voice" : "label"
|
||||||
}`
|
}`
|
||||||
)}
|
)}
|
||||||
autofocus
|
dialogInitialFocus
|
||||||
|
iconTrailing
|
||||||
>
|
>
|
||||||
${SpeechRecognition
|
${SpeechRecognition
|
||||||
? html`
|
? html`
|
||||||
<span suffix="" slot="suffix">
|
<span slot="trailingIcon">
|
||||||
${this.results
|
${this.results
|
||||||
? html`
|
? html`
|
||||||
<div class="bouncer">
|
<div class="bouncer">
|
||||||
@ -155,7 +156,7 @@ export class HaVoiceCommandDialog extends LitElement {
|
|||||||
</span>
|
</span>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</paper-input>
|
</ha-textfield>
|
||||||
${this._agentInfo && this._agentInfo.attribution
|
${this._agentInfo && this._agentInfo.attribution
|
||||||
? html`
|
? html`
|
||||||
<a
|
<a
|
||||||
@ -195,7 +196,7 @@ export class HaVoiceCommandDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleKeyUp(ev: KeyboardEvent) {
|
private _handleKeyUp(ev: KeyboardEvent) {
|
||||||
const input = ev.target as PaperInputElement;
|
const input = ev.target as HaTextField;
|
||||||
if (ev.keyCode === 13 && input.value) {
|
if (ev.keyCode === 13 && input.value) {
|
||||||
this._processText(input.value);
|
this._processText(input.value);
|
||||||
input.value = "";
|
input.value = "";
|
||||||
@ -327,6 +328,7 @@ export class HaVoiceCommandDialog extends LitElement {
|
|||||||
css`
|
css`
|
||||||
ha-icon-button {
|
ha-icon-button {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
|
margin-right: -24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-icon-button[active] {
|
ha-icon-button[active] {
|
||||||
@ -338,7 +340,9 @@ export class HaVoiceCommandDialog extends LitElement {
|
|||||||
--secondary-action-button-flex: 0;
|
--secondary-action-button-flex: 0;
|
||||||
--mdc-dialog-max-width: 450px;
|
--mdc-dialog-max-width: 450px;
|
||||||
}
|
}
|
||||||
|
ha-textfield {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
a.button {
|
a.button {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
@ -406,7 +410,6 @@ export class HaVoiceCommandDialog extends LitElement {
|
|||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
|
||||||
}
|
}
|
||||||
.double-bounce1,
|
.double-bounce1,
|
||||||
.double-bounce2 {
|
.double-bounce2 {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@polymer/paper-input/paper-textarea";
|
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
|
||||||
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/entity/ha-entity-toggle";
|
import "../../../components/entity/ha-entity-toggle";
|
||||||
@ -11,6 +10,7 @@ import "../../../components/ha-circular-progress";
|
|||||||
import "../../../components/ha-markdown";
|
import "../../../components/ha-markdown";
|
||||||
import "../../../components/ha-selector/ha-selector";
|
import "../../../components/ha-selector/ha-selector";
|
||||||
import "../../../components/ha-settings-row";
|
import "../../../components/ha-settings-row";
|
||||||
|
import "../../../components/ha-textfield";
|
||||||
import {
|
import {
|
||||||
BlueprintAutomationConfig,
|
BlueprintAutomationConfig,
|
||||||
triggerAutomationActions,
|
triggerAutomationActions,
|
||||||
@ -38,6 +38,8 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
|
|
||||||
@state() private _blueprints?: Blueprints;
|
@state() private _blueprints?: Blueprints;
|
||||||
|
|
||||||
|
@state() private _showDescription = false;
|
||||||
|
|
||||||
protected firstUpdated(changedProps) {
|
protected firstUpdated(changedProps) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
this._getBlueprints();
|
this._getBlueprints();
|
||||||
@ -50,6 +52,17 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
return this._blueprints[this.config.use_blueprint.path];
|
return this._blueprints[this.config.use_blueprint.path];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected willUpdate(changedProps: PropertyValues): void {
|
||||||
|
super.willUpdate(changedProps);
|
||||||
|
if (
|
||||||
|
!this._showDescription &&
|
||||||
|
changedProps.has("config") &&
|
||||||
|
this.config.description
|
||||||
|
) {
|
||||||
|
this._showDescription = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const blueprint = this._blueprint;
|
const blueprint = this._blueprint;
|
||||||
return html`
|
return html`
|
||||||
@ -64,16 +77,18 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
</span>
|
</span>
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<paper-input
|
<ha-textfield
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.alias"
|
"ui.panel.config.automation.editor.alias"
|
||||||
)}
|
)}
|
||||||
name="alias"
|
name="alias"
|
||||||
.value=${this.config.alias}
|
.value=${this.config.alias || ""}
|
||||||
@value-changed=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
>
|
>
|
||||||
</paper-input>
|
</ha-textfield>
|
||||||
<paper-textarea
|
${this._showDescription
|
||||||
|
? html`
|
||||||
|
<ha-textarea
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.description.label"
|
"ui.panel.config.automation.editor.description.label"
|
||||||
)}
|
)}
|
||||||
@ -81,9 +96,20 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
"ui.panel.config.automation.editor.description.placeholder"
|
"ui.panel.config.automation.editor.description.placeholder"
|
||||||
)}
|
)}
|
||||||
name="description"
|
name="description"
|
||||||
.value=${this.config.description}
|
autogrow
|
||||||
@value-changed=${this._valueChanged}
|
.value=${this.config.description || ""}
|
||||||
></paper-textarea>
|
@change=${this._valueChanged}
|
||||||
|
></ha-textarea>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<div class="link-button-row">
|
||||||
|
<button class="link" @click=${this._addDescription}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.description.add"
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
</div>
|
</div>
|
||||||
${this.stateObj
|
${this.stateObj
|
||||||
? html`
|
? html`
|
||||||
@ -173,15 +199,14 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
value?.default}
|
value?.default}
|
||||||
@value-changed=${this._inputChanged}
|
@value-changed=${this._inputChanged}
|
||||||
></ha-selector>`
|
></ha-selector>`
|
||||||
: html`<paper-input
|
: html`<ha-textfield
|
||||||
.key=${key}
|
.key=${key}
|
||||||
required
|
required
|
||||||
.value=${(this.config.use_blueprint.input &&
|
.value=${(this.config.use_blueprint.input &&
|
||||||
this.config.use_blueprint.input[key]) ??
|
this.config.use_blueprint.input[key]) ??
|
||||||
value?.default}
|
value?.default}
|
||||||
@value-changed=${this._inputChanged}
|
@input=${this._inputChanged}
|
||||||
no-label-float
|
></ha-textfield>`}
|
||||||
></paper-input>`}
|
|
||||||
</ha-settings-row>`
|
</ha-settings-row>`
|
||||||
)
|
)
|
||||||
: html`<p class="padding">
|
: html`<p class="padding">
|
||||||
@ -221,7 +246,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const target = ev.target as any;
|
const target = ev.target as any;
|
||||||
const key = target.key;
|
const key = target.key;
|
||||||
const value = ev.detail.value;
|
const value = ev.detail?.value || target.value;
|
||||||
if (
|
if (
|
||||||
(this.config.use_blueprint.input &&
|
(this.config.use_blueprint.input &&
|
||||||
this.config.use_blueprint.input[key] === value) ||
|
this.config.use_blueprint.input[key] === value) ||
|
||||||
@ -262,6 +287,10 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _addDescription() {
|
||||||
|
this._showDescription = true;
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
@ -273,9 +302,16 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
.padding {
|
.padding {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
.link-button-row {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
.blueprint-picker-container {
|
.blueprint-picker-container {
|
||||||
padding: 0 16px 16px;
|
padding: 0 16px 16px;
|
||||||
}
|
}
|
||||||
|
ha-textarea,
|
||||||
|
ha-textfield {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
h3 {
|
h3 {
|
||||||
margin: 16px;
|
margin: 16px;
|
||||||
}
|
}
|
||||||
@ -292,9 +328,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
--paper-time-input-justify-content: flex-end;
|
--paper-time-input-justify-content: flex-end;
|
||||||
border-top: 1px solid var(--divider-color);
|
border-top: 1px solid var(--divider-color);
|
||||||
}
|
}
|
||||||
:host(:not([narrow])) ha-settings-row paper-input {
|
:host(:not([narrow])) ha-settings-row ha-textfield,
|
||||||
width: 60%;
|
|
||||||
}
|
|
||||||
:host(:not([narrow])) ha-settings-row ha-selector {
|
:host(:not([narrow])) ha-settings-row ha-selector {
|
||||||
width: 60%;
|
width: 60%;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state, query } from "lit/decorators";
|
import { customElement, property, query, 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 "../../../../components/ha-dialog";
|
||||||
|
import "../../../../components/ha-textfield";
|
||||||
|
import type { HaTextField } from "../../../../components/ha-textfield";
|
||||||
import type { AutomationConfig } from "../../../../data/automation";
|
import type { AutomationConfig } from "../../../../data/automation";
|
||||||
import { convertThingTalk } from "../../../../data/cloud";
|
import { convertThingTalk } from "../../../../data/cloud";
|
||||||
import { haStyle, haStyleDialog } from "../../../../resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../resources/styles";
|
||||||
@ -12,7 +13,6 @@ import type { HomeAssistant } from "../../../../types";
|
|||||||
import "./ha-thingtalk-placeholders";
|
import "./ha-thingtalk-placeholders";
|
||||||
import type { PlaceholderValues } from "./ha-thingtalk-placeholders";
|
import type { PlaceholderValues } from "./ha-thingtalk-placeholders";
|
||||||
import type { ThingtalkDialogParams } from "./show-dialog-thingtalk";
|
import type { ThingtalkDialogParams } from "./show-dialog-thingtalk";
|
||||||
import "../../../../components/ha-dialog";
|
|
||||||
|
|
||||||
export interface Placeholder {
|
export interface Placeholder {
|
||||||
name: string;
|
name: string;
|
||||||
@ -38,7 +38,7 @@ class DialogThingtalk extends LitElement {
|
|||||||
|
|
||||||
@state() private _placeholders?: PlaceholderContainer;
|
@state() private _placeholders?: PlaceholderContainer;
|
||||||
|
|
||||||
@query("#input") private _input?: PaperInputElement;
|
@query("#input") private _input?: HaTextField;
|
||||||
|
|
||||||
private _value?: string;
|
private _value?: string;
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ class DialogThingtalk extends LitElement {
|
|||||||
this._placeholders = undefined;
|
this._placeholders = undefined;
|
||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
if (this._input) {
|
if (this._input) {
|
||||||
this._input.value = null;
|
this._input.value = "";
|
||||||
}
|
}
|
||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
}
|
}
|
||||||
@ -127,13 +127,13 @@ class DialogThingtalk extends LitElement {
|
|||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<paper-input
|
<ha-textfield
|
||||||
id="input"
|
id="input"
|
||||||
label="What should this automation do?"
|
label="What should this automation do?"
|
||||||
.value=${this._value}
|
.value=${this._value}
|
||||||
autofocus
|
autofocus
|
||||||
@keyup=${this._handleKeyUp}
|
@keyup=${this._handleKeyUp}
|
||||||
></paper-input>
|
></ha-textfield>
|
||||||
<a
|
<a
|
||||||
href="https://almond.stanford.edu/"
|
href="https://almond.stanford.edu/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
@ -88,7 +88,7 @@ export class HaWebhookTrigger extends LitElement {
|
|||||||
.helper=${this.hass.localize(
|
.helper=${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.type.webhook.webhook_id_helper"
|
"ui.panel.config.automation.editor.triggers.type.webhook.webhook_id_helper"
|
||||||
)}
|
)}
|
||||||
.iconTrailing=${true}
|
iconTrailing
|
||||||
.value=${webhookId || ""}
|
.value=${webhookId || ""}
|
||||||
@input=${this._valueChanged}
|
@input=${this._valueChanged}
|
||||||
>
|
>
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
|
||||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||||
import { state } from "lit/decorators";
|
import { query, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
|
||||||
|
import type { HaTextField } from "../../../../components/ha-textfield";
|
||||||
|
import "../../../../components/ha-textfield";
|
||||||
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../resources/styles";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { documentationUrl } from "../../../../util/documentation-url";
|
import { documentationUrl } from "../../../../util/documentation-url";
|
||||||
import { WebhookDialogParams } from "./show-dialog-manage-cloudhook";
|
import { WebhookDialogParams } from "./show-dialog-manage-cloudhook";
|
||||||
@ -17,6 +18,8 @@ export class DialogManageCloudhook extends LitElement {
|
|||||||
|
|
||||||
@state() private _params?: WebhookDialogParams;
|
@state() private _params?: WebhookDialogParams;
|
||||||
|
|
||||||
|
@query("ha-textfield") _input!: HaTextField;
|
||||||
|
|
||||||
public showDialog(params: WebhookDialogParams) {
|
public showDialog(params: WebhookDialogParams) {
|
||||||
this._params = params;
|
this._params = params;
|
||||||
}
|
}
|
||||||
@ -53,12 +56,12 @@ export class DialogManageCloudhook extends LitElement {
|
|||||||
"ui.panel.config.cloud.dialog_cloudhook.available_at"
|
"ui.panel.config.cloud.dialog_cloudhook.available_at"
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<paper-input
|
<ha-textfield
|
||||||
label=${inputLabel}
|
.label=${inputLabel}
|
||||||
value=${cloudhook.cloudhook_url}
|
.value=${cloudhook.cloudhook_url}
|
||||||
@click=${this._copyClipboard}
|
@click=${this._copyClipboard}
|
||||||
@blur=${this._restoreLabel}
|
@blur=${this._restoreLabel}
|
||||||
></paper-input>
|
></ha-textfield>
|
||||||
<p>
|
<p>
|
||||||
${cloudhook.managed
|
${cloudhook.managed
|
||||||
? html`
|
? html`
|
||||||
@ -98,10 +101,6 @@ export class DialogManageCloudhook extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _paperInput(): PaperInputElement {
|
|
||||||
return this.shadowRoot!.querySelector("paper-input")!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _disableWebhook() {
|
private async _disableWebhook() {
|
||||||
showConfirmationDialog(this, {
|
showConfirmationDialog(this, {
|
||||||
text: this.hass!.localize(
|
text: this.hass!.localize(
|
||||||
@ -117,14 +116,10 @@ export class DialogManageCloudhook extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _copyClipboard(ev: FocusEvent) {
|
private _copyClipboard(ev: FocusEvent) {
|
||||||
// paper-input -> iron-input -> input
|
const textField = ev.currentTarget as HaTextField;
|
||||||
const paperInput = ev.currentTarget as PaperInputElement;
|
|
||||||
const input = (paperInput.inputElement as any)
|
|
||||||
.inputElement as HTMLInputElement;
|
|
||||||
input.setSelectionRange(0, input.value.length);
|
|
||||||
try {
|
try {
|
||||||
document.execCommand("copy");
|
copyToClipboard(textField.value);
|
||||||
paperInput.label = this.hass!.localize(
|
textField.label = this.hass!.localize(
|
||||||
"ui.panel.config.cloud.dialog_cloudhook.copied_to_clipboard"
|
"ui.panel.config.cloud.dialog_cloudhook.copied_to_clipboard"
|
||||||
);
|
);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@ -133,18 +128,19 @@ export class DialogManageCloudhook extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _restoreLabel() {
|
private _restoreLabel() {
|
||||||
this._paperInput.label = inputLabel;
|
this._input.label = inputLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
ha-dialog {
|
ha-dialog {
|
||||||
width: 650px;
|
width: 650px;
|
||||||
}
|
}
|
||||||
paper-input {
|
ha-textfield {
|
||||||
margin-top: -8px;
|
display: block;
|
||||||
}
|
}
|
||||||
button.link {
|
button.link {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
|
@ -19,6 +19,7 @@ import "../../../../components/entity/ha-statistic-picker";
|
|||||||
import "../../../../components/entity/ha-entity-picker";
|
import "../../../../components/entity/ha-entity-picker";
|
||||||
import "../../../../components/ha-radio";
|
import "../../../../components/ha-radio";
|
||||||
import "../../../../components/ha-formfield";
|
import "../../../../components/ha-formfield";
|
||||||
|
import "../../../../components/ha-textfield";
|
||||||
import type { HaRadio } from "../../../../components/ha-radio";
|
import type { HaRadio } from "../../../../components/ha-radio";
|
||||||
|
|
||||||
@customElement("dialog-energy-gas-settings")
|
@customElement("dialog-energy-gas-settings")
|
||||||
@ -188,20 +189,19 @@ export class DialogEnergyGasSettings
|
|||||||
></ha-radio>
|
></ha-radio>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
${this._costs === "number"
|
${this._costs === "number"
|
||||||
? html`<paper-input
|
? html`<ha-textfield
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
`ui.panel.config.energy.gas.dialog.cost_number_input`,
|
`ui.panel.config.energy.gas.dialog.cost_number_input`,
|
||||||
{ unit }
|
{ unit }
|
||||||
)}
|
)}
|
||||||
no-label-float
|
|
||||||
class="price-options"
|
class="price-options"
|
||||||
step=".01"
|
step=".01"
|
||||||
type="number"
|
type="number"
|
||||||
.value=${this._source.number_energy_price}
|
.value=${this._source.number_energy_price}
|
||||||
@value-changed=${this._numberPriceChanged}
|
@change=${this._numberPriceChanged}
|
||||||
|
.suffix=${`${this.hass.config.currency}/${unit}`}
|
||||||
>
|
>
|
||||||
<span slot="suffix">${this.hass.config.currency}/${unit}</span>
|
</ha-textfield>`
|
||||||
</paper-input>`
|
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<mwc-button @click=${this.closeDialog} slot="secondaryAction">
|
<mwc-button @click=${this.closeDialog} slot="secondaryAction">
|
||||||
@ -223,10 +223,10 @@ export class DialogEnergyGasSettings
|
|||||||
this._costs = input.value as any;
|
this._costs = input.value as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _numberPriceChanged(ev: CustomEvent) {
|
private _numberPriceChanged(ev) {
|
||||||
this._source = {
|
this._source = {
|
||||||
...this._source!,
|
...this._source!,
|
||||||
number_energy_price: Number(ev.detail.value),
|
number_energy_price: Number(ev.target.value),
|
||||||
entity_energy_price: null,
|
entity_energy_price: null,
|
||||||
stat_cost: null,
|
stat_cost: null,
|
||||||
};
|
};
|
||||||
@ -295,13 +295,10 @@ export class DialogEnergyGasSettings
|
|||||||
ha-formfield {
|
ha-formfield {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
ha-statistic-picker {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.price-options {
|
.price-options {
|
||||||
display: block;
|
display: block;
|
||||||
padding-left: 52px;
|
padding-left: 52px;
|
||||||
margin-top: -16px;
|
margin-top: -8px;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@ -190,24 +190,21 @@ export class DialogEnergyGridFlowSettings
|
|||||||
></ha-radio>
|
></ha-radio>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
${this._costs === "number"
|
${this._costs === "number"
|
||||||
? html`<paper-input
|
? html`<ha-textfield
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
`ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.cost_number_input`
|
`ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.cost_number_input`
|
||||||
)}
|
)}
|
||||||
no-label-float
|
|
||||||
class="price-options"
|
class="price-options"
|
||||||
step=".01"
|
step=".01"
|
||||||
type="number"
|
type="number"
|
||||||
.value=${this._source.number_energy_price}
|
.value=${this._source.number_energy_price}
|
||||||
@value-changed=${this._numberPriceChanged}
|
.suffix=${this.hass.localize(
|
||||||
>
|
|
||||||
<span slot="suffix"
|
|
||||||
>${this.hass.localize(
|
|
||||||
`ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.cost_number_suffix`,
|
`ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.cost_number_suffix`,
|
||||||
{ currency: this.hass.config.currency }
|
{ currency: this.hass.config.currency }
|
||||||
)}</span
|
)}
|
||||||
|
@change=${this._numberPriceChanged}
|
||||||
>
|
>
|
||||||
</paper-input>`
|
</ha-textfield>`
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<mwc-button @click=${this.closeDialog} slot="secondaryAction">
|
<mwc-button @click=${this.closeDialog} slot="secondaryAction">
|
||||||
@ -302,13 +299,10 @@ export class DialogEnergyGridFlowSettings
|
|||||||
ha-formfield {
|
ha-formfield {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
ha-statistic-picker {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.price-options {
|
.price-options {
|
||||||
display: block;
|
display: block;
|
||||||
padding-left: 52px;
|
padding-left: 52px;
|
||||||
margin-top: -16px;
|
margin-top: -8px;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@ -30,6 +30,7 @@ import "../../../components/ha-check-list-item";
|
|||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
|
import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
|
||||||
import {
|
import {
|
||||||
|
getConfigFlowHandlers,
|
||||||
getConfigFlowInProgressCollection,
|
getConfigFlowInProgressCollection,
|
||||||
localizeConfigFlowTitle,
|
localizeConfigFlowTitle,
|
||||||
subscribeConfigFlowInProgress,
|
subscribeConfigFlowInProgress,
|
||||||
@ -51,7 +52,10 @@ import {
|
|||||||
} from "../../../data/integration";
|
} from "../../../data/integration";
|
||||||
import { scanUSBDevices } from "../../../data/usb";
|
import { scanUSBDevices } from "../../../data/usb";
|
||||||
import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow";
|
import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow";
|
||||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
import {
|
||||||
|
showAlertDialog,
|
||||||
|
showConfirmationDialog,
|
||||||
|
} from "../../../dialogs/generic/show-dialog-box";
|
||||||
import "../../../layouts/hass-loading-screen";
|
import "../../../layouts/hass-loading-screen";
|
||||||
import "../../../layouts/hass-tabs-subpage";
|
import "../../../layouts/hass-tabs-subpage";
|
||||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
@ -652,6 +656,19 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
if (!domain) {
|
if (!domain) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const handlers = await getConfigFlowHandlers(this.hass);
|
||||||
|
|
||||||
|
if (!handlers.includes(domain)) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_flow.error"
|
||||||
|
),
|
||||||
|
text: this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_flow.no_config_flow"
|
||||||
|
),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
const localize = await localizePromise;
|
const localize = await localizePromise;
|
||||||
if (
|
if (
|
||||||
!(await showConfirmationDialog(this, {
|
!(await showConfirmationDialog(this, {
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import { mdiAlertCircle, mdiCheckCircle, mdiQrcodeScan } from "@mdi/js";
|
import { mdiAlertCircle, mdiCheckCircle, mdiQrcodeScan } from "@mdi/js";
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
@ -11,28 +9,30 @@ import { HaCheckbox } from "../../../../../components/ha-checkbox";
|
|||||||
import "../../../../../components/ha-circular-progress";
|
import "../../../../../components/ha-circular-progress";
|
||||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||||
import "../../../../../components/ha-formfield";
|
import "../../../../../components/ha-formfield";
|
||||||
|
import "../../../../../components/ha-qr-scanner";
|
||||||
import "../../../../../components/ha-radio";
|
import "../../../../../components/ha-radio";
|
||||||
import "../../../../../components/ha-switch";
|
import "../../../../../components/ha-switch";
|
||||||
|
import "../../../../../components/ha-textfield";
|
||||||
|
import type { HaTextField } from "../../../../../components/ha-textfield";
|
||||||
import {
|
import {
|
||||||
zwaveGrantSecurityClasses,
|
|
||||||
InclusionStrategy,
|
InclusionStrategy,
|
||||||
MINIMUM_QR_STRING_LENGTH,
|
MINIMUM_QR_STRING_LENGTH,
|
||||||
zwaveParseQrCode,
|
PlannedProvisioningEntry,
|
||||||
provisionZwaveSmartStartNode,
|
provisionZwaveSmartStartNode,
|
||||||
QRProvisioningInformation,
|
QRProvisioningInformation,
|
||||||
RequestedGrant,
|
RequestedGrant,
|
||||||
SecurityClass,
|
SecurityClass,
|
||||||
stopZwaveInclusion,
|
stopZwaveInclusion,
|
||||||
subscribeAddZwaveNode,
|
subscribeAddZwaveNode,
|
||||||
|
ZWaveFeature,
|
||||||
|
zwaveGrantSecurityClasses,
|
||||||
|
zwaveParseQrCode,
|
||||||
zwaveSupportsFeature,
|
zwaveSupportsFeature,
|
||||||
zwaveValidateDskAndEnterPin,
|
zwaveValidateDskAndEnterPin,
|
||||||
ZWaveFeature,
|
|
||||||
PlannedProvisioningEntry,
|
|
||||||
} from "../../../../../data/zwave_js";
|
} from "../../../../../data/zwave_js";
|
||||||
import { haStyle, haStyleDialog } from "../../../../../resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../../resources/styles";
|
||||||
import { HomeAssistant } from "../../../../../types";
|
import { HomeAssistant } from "../../../../../types";
|
||||||
import { ZWaveJSAddNodeDialogParams } from "./show-dialog-zwave_js-add-node";
|
import { ZWaveJSAddNodeDialogParams } from "./show-dialog-zwave_js-add-node";
|
||||||
import "../../../../../components/ha-qr-scanner";
|
|
||||||
|
|
||||||
export interface ZWaveJSAddNodeDevice {
|
export interface ZWaveJSAddNodeDevice {
|
||||||
id: string;
|
id: string;
|
||||||
@ -98,7 +98,7 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||||||
this._startInclusion();
|
this._startInclusion();
|
||||||
}
|
}
|
||||||
|
|
||||||
@query("#pin-input") private _pinInput?: PaperInputElement;
|
@query("#pin-input") private _pinInput?: HaTextField;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._entryId) {
|
if (!this._entryId) {
|
||||||
@ -202,12 +202,11 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
<paper-input
|
<ha-textfield
|
||||||
label="PIN"
|
label="PIN"
|
||||||
id="pin-input"
|
id="pin-input"
|
||||||
@keyup=${this._handlePinKeyUp}
|
@keyup=${this._handlePinKeyUp}
|
||||||
no-label-float
|
></ha-textfield>
|
||||||
></paper-input>
|
|
||||||
${this._dsk}
|
${this._dsk}
|
||||||
</div>
|
</div>
|
||||||
<mwc-button
|
<mwc-button
|
||||||
@ -814,6 +813,9 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||||||
width: 68px;
|
width: 68px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
}
|
}
|
||||||
|
ha-textfield {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
.secondary {
|
.secondary {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import "../../../../../components/ha-select";
|
|||||||
import "../../../../../components/ha-settings-row";
|
import "../../../../../components/ha-settings-row";
|
||||||
import "../../../../../components/ha-svg-icon";
|
import "../../../../../components/ha-svg-icon";
|
||||||
import "../../../../../components/ha-switch";
|
import "../../../../../components/ha-switch";
|
||||||
|
import "../../../../../components/ha-textfield";
|
||||||
import {
|
import {
|
||||||
computeDeviceName,
|
computeDeviceName,
|
||||||
DeviceRegistryEntry,
|
DeviceRegistryEntry,
|
||||||
@ -265,7 +266,7 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
if (item.configuration_value_type === "manual_entry") {
|
if (item.configuration_value_type === "manual_entry") {
|
||||||
return html`${labelAndDescription}
|
return html`${labelAndDescription}
|
||||||
<paper-input
|
<ha-textfield
|
||||||
type="number"
|
type="number"
|
||||||
.value=${item.value}
|
.value=${item.value}
|
||||||
.min=${item.metadata.min}
|
.min=${item.metadata.min}
|
||||||
@ -274,12 +275,12 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
.propertyKey=${item.property_key}
|
.propertyKey=${item.property_key}
|
||||||
.key=${id}
|
.key=${id}
|
||||||
.disabled=${!item.metadata.writeable}
|
.disabled=${!item.metadata.writeable}
|
||||||
@value-changed=${this._numericInputChanged}
|
@input=${this._numericInputChanged}
|
||||||
>
|
>
|
||||||
${item.metadata.unit
|
${item.metadata.unit
|
||||||
? html`<span slot="suffix">${item.metadata.unit}</span>`
|
? html`<span slot="suffix">${item.metadata.unit}</span>`
|
||||||
: ""}
|
: ""}
|
||||||
</paper-input> `;
|
</ha-textfield>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.configuration_value_type === "enumerated") {
|
if (item.configuration_value_type === "enumerated") {
|
||||||
@ -492,7 +493,7 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host(:not([narrow])) ha-settings-row paper-input {
|
:host(:not([narrow])) ha-settings-row ha-textfield {
|
||||||
width: 30%;
|
width: 30%;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,18 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { slugify } from "../../../../common/string/slugify";
|
import { slugify } from "../../../../common/string/slugify";
|
||||||
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
|
||||||
import { createCloseHeading } from "../../../../components/ha-dialog";
|
import { createCloseHeading } from "../../../../components/ha-dialog";
|
||||||
import "../../../../components/ha-formfield";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import "../../../../components/ha-icon-picker";
|
import { HaFormSchema } from "../../../../components/ha-form/types";
|
||||||
import "../../../../components/ha-switch";
|
import { CoreFrontendUserData } from "../../../../data/frontend";
|
||||||
import type { HaSwitch } from "../../../../components/ha-switch";
|
|
||||||
import {
|
import {
|
||||||
LovelaceDashboard,
|
LovelaceDashboard,
|
||||||
LovelaceDashboardCreateParams,
|
LovelaceDashboardCreateParams,
|
||||||
LovelaceDashboardMutableParams,
|
|
||||||
} from "../../../../data/lovelace";
|
} from "../../../../data/lovelace";
|
||||||
import { DEFAULT_PANEL, setDefaultPanel } from "../../../../data/panel";
|
import { DEFAULT_PANEL, setDefaultPanel } from "../../../../data/panel";
|
||||||
import { PolymerChangedEvent } from "../../../../polymer-types";
|
|
||||||
import { haStyleDialog } from "../../../../resources/styles";
|
import { haStyleDialog } from "../../../../resources/styles";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { LovelaceDashboardDetailsDialogParams } from "./show-dialog-lovelace-dashboard-detail";
|
import { LovelaceDashboardDetailsDialogParams } from "./show-dialog-lovelace-dashboard-detail";
|
||||||
@ -25,62 +23,54 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
|||||||
|
|
||||||
@state() private _params?: LovelaceDashboardDetailsDialogParams;
|
@state() private _params?: LovelaceDashboardDetailsDialogParams;
|
||||||
|
|
||||||
@state() private _urlPath!: LovelaceDashboard["url_path"];
|
@state() private _urlPathChanged = false;
|
||||||
|
|
||||||
@state() private _showInSidebar!: boolean;
|
@state() private _data?: Partial<LovelaceDashboard>;
|
||||||
|
|
||||||
@state() private _icon!: string;
|
@state() private _error?: Record<string, string>;
|
||||||
|
|
||||||
@state() private _title!: string;
|
|
||||||
|
|
||||||
@state()
|
|
||||||
private _requireAdmin!: LovelaceDashboard["require_admin"];
|
|
||||||
|
|
||||||
@state() private _error?: string;
|
|
||||||
|
|
||||||
@state() private _submitting = false;
|
@state() private _submitting = false;
|
||||||
|
|
||||||
public async showDialog(
|
public showDialog(params: LovelaceDashboardDetailsDialogParams): void {
|
||||||
params: LovelaceDashboardDetailsDialogParams
|
|
||||||
): Promise<void> {
|
|
||||||
this._params = params;
|
this._params = params;
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._urlPath = this._params.urlPath || "";
|
this._urlPathChanged = false;
|
||||||
if (this._params.dashboard) {
|
if (this._params.dashboard) {
|
||||||
this._showInSidebar = !!this._params.dashboard.show_in_sidebar;
|
this._data = this._params.dashboard;
|
||||||
this._icon = this._params.dashboard.icon || "";
|
|
||||||
this._title = this._params.dashboard.title || "";
|
|
||||||
this._requireAdmin = this._params.dashboard.require_admin || false;
|
|
||||||
} else {
|
} else {
|
||||||
this._showInSidebar = true;
|
this._data = {
|
||||||
this._icon = "";
|
show_in_sidebar: true,
|
||||||
this._title = "";
|
icon: "",
|
||||||
this._requireAdmin = false;
|
title: "",
|
||||||
|
require_admin: false,
|
||||||
|
mode: "storage",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
await this.updateComplete;
|
}
|
||||||
|
|
||||||
|
public closeDialog(): void {
|
||||||
|
this._params = undefined;
|
||||||
|
this._data = undefined;
|
||||||
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._params) {
|
if (!this._params || !this._data) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
const defaultPanelUrlPath = this.hass.defaultPanel;
|
const defaultPanelUrlPath = this.hass.defaultPanel;
|
||||||
const urlInvalid =
|
const titleInvalid = !this._data.title || !this._data.title.trim();
|
||||||
this._params.urlPath !== "lovelace" &&
|
|
||||||
!/^[a-zA-Z0-9_-]+-[a-zA-Z0-9_-]+$/.test(this._urlPath);
|
|
||||||
const titleInvalid = !this._title.trim();
|
|
||||||
const dir = computeRTLDirection(this.hass);
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-dialog
|
||||||
open
|
open
|
||||||
@closed=${this._close}
|
@closed=${this.closeDialog}
|
||||||
scrimClickAction
|
scrimClickAction
|
||||||
escapeKeyAction
|
escapeKeyAction
|
||||||
.heading=${createCloseHeading(
|
.heading=${createCloseHeading(
|
||||||
this.hass,
|
this.hass,
|
||||||
this._params.urlPath
|
this._params.urlPath
|
||||||
? this._title ||
|
? this._data.title ||
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
"ui.panel.config.lovelace.dashboards.detail.edit_dashboard"
|
"ui.panel.config.lovelace.dashboards.detail.edit_dashboard"
|
||||||
)
|
)
|
||||||
@ -99,76 +89,14 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
|||||||
"ui.panel.config.lovelace.dashboards.cant_edit_default"
|
"ui.panel.config.lovelace.dashboards.cant_edit_default"
|
||||||
)
|
)
|
||||||
: html`
|
: html`
|
||||||
${this._error
|
<ha-form
|
||||||
? html` <div class="error">${this._error}</div> `
|
.schema=${this._schema(this._params, this.hass.userData)}
|
||||||
: ""}
|
.data=${this._data}
|
||||||
<div class="form">
|
.hass=${this.hass}
|
||||||
<paper-input
|
.error=${this._error}
|
||||||
.value=${this._title}
|
.computeLabel=${this._computeLabel}
|
||||||
@value-changed=${this._titleChanged}
|
@value-changed=${this._valueChanged}
|
||||||
.label=${this.hass.localize(
|
></ha-form>
|
||||||
"ui.panel.config.lovelace.dashboards.detail.title"
|
|
||||||
)}
|
|
||||||
@blur=${this.hass.userData?.showAdvanced
|
|
||||||
? this._fillUrlPath
|
|
||||||
: undefined}
|
|
||||||
.invalid=${titleInvalid}
|
|
||||||
.errorMessage=${this.hass.localize(
|
|
||||||
"ui.panel.config.lovelace.dashboards.detail.title_required"
|
|
||||||
)}
|
|
||||||
dialogInitialFocus
|
|
||||||
></paper-input>
|
|
||||||
<ha-icon-picker
|
|
||||||
.value=${this._icon}
|
|
||||||
@value-changed=${this._iconChanged}
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.lovelace.dashboards.detail.icon"
|
|
||||||
)}
|
|
||||||
></ha-icon-picker>
|
|
||||||
${!this._params.dashboard && this.hass.userData?.showAdvanced
|
|
||||||
? html`
|
|
||||||
<paper-input
|
|
||||||
.value=${this._urlPath}
|
|
||||||
@value-changed=${this._urlChanged}
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.lovelace.dashboards.detail.url"
|
|
||||||
)}
|
|
||||||
.errorMessage=${this.hass.localize(
|
|
||||||
"ui.panel.config.lovelace.dashboards.detail.url_error_msg"
|
|
||||||
)}
|
|
||||||
.invalid=${urlInvalid}
|
|
||||||
></paper-input>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
<div>
|
|
||||||
<ha-formfield
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.lovelace.dashboards.detail.show_sidebar"
|
|
||||||
)}
|
|
||||||
.dir=${dir}
|
|
||||||
>
|
|
||||||
<ha-switch
|
|
||||||
.checked=${this._showInSidebar}
|
|
||||||
@change=${this._showSidebarChanged}
|
|
||||||
>
|
|
||||||
</ha-switch>
|
|
||||||
</ha-formfield>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<ha-formfield
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.lovelace.dashboards.detail.require_admin"
|
|
||||||
)}
|
|
||||||
.dir=${dir}
|
|
||||||
>
|
|
||||||
<ha-switch
|
|
||||||
.checked=${this._requireAdmin}
|
|
||||||
@change=${this._requireAdminChanged}
|
|
||||||
>
|
|
||||||
</ha-switch>
|
|
||||||
</ha-formfield>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`}
|
`}
|
||||||
</div>
|
</div>
|
||||||
${this._params.urlPath
|
${this._params.urlPath
|
||||||
@ -206,7 +134,9 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
|||||||
<mwc-button
|
<mwc-button
|
||||||
slot="primaryAction"
|
slot="primaryAction"
|
||||||
@click=${this._updateDashboard}
|
@click=${this._updateDashboard}
|
||||||
.disabled=${urlInvalid || titleInvalid || this._submitting}
|
.disabled=${(this._error && "url_path" in this._error) ||
|
||||||
|
titleInvalid ||
|
||||||
|
this._submitting}
|
||||||
dialogInitialFocus
|
dialogInitialFocus
|
||||||
>
|
>
|
||||||
${this._params.urlPath
|
${this._params.urlPath
|
||||||
@ -223,41 +153,97 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _urlChanged(ev: PolymerChangedEvent<string>) {
|
private _schema = memoizeOne(
|
||||||
this._error = undefined;
|
(
|
||||||
this._urlPath = ev.detail.value;
|
params: LovelaceDashboardDetailsDialogParams,
|
||||||
}
|
userData: CoreFrontendUserData | null | undefined
|
||||||
|
) =>
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: "title",
|
||||||
|
required: true,
|
||||||
|
selector: {
|
||||||
|
text: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "icon",
|
||||||
|
required: true,
|
||||||
|
selector: {
|
||||||
|
icon: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
!params.dashboard &&
|
||||||
|
userData?.showAdvanced && {
|
||||||
|
name: "url_path",
|
||||||
|
required: true,
|
||||||
|
selector: { text: {} },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "require_admin",
|
||||||
|
required: true,
|
||||||
|
selector: {
|
||||||
|
boolean: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "show_in_sidebar",
|
||||||
|
required: true,
|
||||||
|
selector: {
|
||||||
|
boolean: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
].filter(Boolean)
|
||||||
|
);
|
||||||
|
|
||||||
private _iconChanged(ev: PolymerChangedEvent<string>) {
|
private _computeLabel = (entry: HaFormSchema): string =>
|
||||||
this._error = undefined;
|
this.hass.localize(
|
||||||
this._icon = ev.detail.value;
|
`ui.panel.config.lovelace.dashboards.detail.${
|
||||||
}
|
entry.name === "show_in_sidebar"
|
||||||
|
? "show_sidebar"
|
||||||
|
: entry.name === "url_path"
|
||||||
|
? "url"
|
||||||
|
: entry.name
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
|
||||||
private _titleChanged(ev: PolymerChangedEvent<string>) {
|
private _valueChanged(ev: CustomEvent) {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._title = ev.detail.value;
|
const value = ev.detail.value;
|
||||||
if (!this.hass.userData?.showAdvanced) {
|
if (value.url_path !== this._data?.url_path) {
|
||||||
this._fillUrlPath();
|
this._urlPathChanged = true;
|
||||||
|
if (
|
||||||
|
!value.url_path ||
|
||||||
|
value.url_path === "lovelace" ||
|
||||||
|
!/^[a-zA-Z0-9_-]+-[a-zA-Z0-9_-]+$/.test(value.url_path)
|
||||||
|
) {
|
||||||
|
this._error = {
|
||||||
|
url_path: this.hass.localize(
|
||||||
|
"ui.panel.config.lovelace.dashboards.detail.url_error_msg"
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value.title !== this._data?.title) {
|
||||||
|
this._data = value;
|
||||||
|
this._fillUrlPath(value.title);
|
||||||
|
} else {
|
||||||
|
this._data = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _fillUrlPath() {
|
private _fillUrlPath(title: string) {
|
||||||
if ((this.hass.userData?.showAdvanced && this._urlPath) || !this._title) {
|
if ((this.hass.userData?.showAdvanced && this._urlPathChanged) || !title) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const slugifyTitle = slugify(this._title, "-");
|
const slugifyTitle = slugify(title, "-");
|
||||||
this._urlPath = slugifyTitle.includes("-")
|
this._data = {
|
||||||
|
...this._data,
|
||||||
|
url_path: slugifyTitle.includes("-")
|
||||||
? slugifyTitle
|
? slugifyTitle
|
||||||
: `lovelace-${slugifyTitle}`;
|
: `lovelace-${slugifyTitle}`,
|
||||||
}
|
};
|
||||||
|
|
||||||
private _showSidebarChanged(ev: Event) {
|
|
||||||
this._showInSidebar = (ev.target as HaSwitch).checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _requireAdminChanged(ev: Event) {
|
|
||||||
this._requireAdmin = (ev.target as HaSwitch).checked;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleDefault() {
|
private _toggleDefault() {
|
||||||
@ -273,29 +259,20 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
|||||||
|
|
||||||
private async _updateDashboard() {
|
private async _updateDashboard() {
|
||||||
if (this._params?.urlPath && !this._params.dashboard?.id) {
|
if (this._params?.urlPath && !this._params.dashboard?.id) {
|
||||||
this._close();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
this._submitting = true;
|
this._submitting = true;
|
||||||
try {
|
try {
|
||||||
const values: Partial<LovelaceDashboardMutableParams> = {
|
|
||||||
require_admin: this._requireAdmin,
|
|
||||||
show_in_sidebar: this._showInSidebar,
|
|
||||||
icon: this._icon || undefined,
|
|
||||||
title: this._title,
|
|
||||||
};
|
|
||||||
if (this._params!.dashboard) {
|
if (this._params!.dashboard) {
|
||||||
await this._params!.updateDashboard(values);
|
await this._params!.updateDashboard(this._data as LovelaceDashboard);
|
||||||
} else {
|
} else {
|
||||||
(values as LovelaceDashboardCreateParams).url_path =
|
|
||||||
this._urlPath.trim();
|
|
||||||
(values as LovelaceDashboardCreateParams).mode = "storage";
|
|
||||||
await this._params!.createDashboard(
|
await this._params!.createDashboard(
|
||||||
values as LovelaceDashboardCreateParams
|
this._data as LovelaceDashboardCreateParams
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this._close();
|
this.closeDialog();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this._error = err?.message || "Unknown error";
|
this._error = { base: err?.message || "Unknown error" };
|
||||||
} finally {
|
} finally {
|
||||||
this._submitting = false;
|
this._submitting = false;
|
||||||
}
|
}
|
||||||
@ -305,26 +282,15 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
|||||||
this._submitting = true;
|
this._submitting = true;
|
||||||
try {
|
try {
|
||||||
if (await this._params!.removeDashboard()) {
|
if (await this._params!.removeDashboard()) {
|
||||||
this._close();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this._submitting = false;
|
this._submitting = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _close(): void {
|
|
||||||
this._params = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [haStyleDialog, css``];
|
||||||
haStyleDialog,
|
|
||||||
css`
|
|
||||||
ha-switch {
|
|
||||||
padding: 16px 0;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
import memoizeOne from "memoize-one";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { createCloseHeading } from "../../../../components/ha-dialog";
|
import { createCloseHeading } from "../../../../components/ha-dialog";
|
||||||
import "../../../../components/ha-select";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import {
|
import { HaFormSchema } from "../../../../components/ha-form/types";
|
||||||
LovelaceResource,
|
import { LovelaceResourcesMutableParams } from "../../../../data/lovelace";
|
||||||
LovelaceResourcesMutableParams,
|
|
||||||
} from "../../../../data/lovelace";
|
|
||||||
import { PolymerChangedEvent } from "../../../../polymer-types";
|
|
||||||
import { haStyleDialog } from "../../../../resources/styles";
|
import { haStyleDialog } from "../../../../resources/styles";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { LovelaceResourceDetailsDialogParams } from "./show-dialog-lovelace-resource-detail";
|
import { LovelaceResourceDetailsDialogParams } from "./show-dialog-lovelace-resource-detail";
|
||||||
|
|
||||||
const detectResourceType = (url: string) => {
|
const detectResourceType = (url?: string) => {
|
||||||
|
if (!url) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
const ext = url.split(".").pop() || "";
|
const ext = url.split(".").pop() || "";
|
||||||
|
|
||||||
if (ext === "css") {
|
if (ext === "css") {
|
||||||
@ -35,38 +34,41 @@ export class DialogLovelaceResourceDetail extends LitElement {
|
|||||||
|
|
||||||
@state() private _params?: LovelaceResourceDetailsDialogParams;
|
@state() private _params?: LovelaceResourceDetailsDialogParams;
|
||||||
|
|
||||||
@state() private _url!: LovelaceResource["url"];
|
@state() private _data?: Partial<LovelaceResourcesMutableParams>;
|
||||||
|
|
||||||
@state() private _type?: LovelaceResource["type"];
|
@state() private _error?: Record<string, string>;
|
||||||
|
|
||||||
@state() private _error?: string;
|
|
||||||
|
|
||||||
@state() private _submitting = false;
|
@state() private _submitting = false;
|
||||||
|
|
||||||
public async showDialog(
|
public showDialog(params: LovelaceResourceDetailsDialogParams): void {
|
||||||
params: LovelaceResourceDetailsDialogParams
|
|
||||||
): Promise<void> {
|
|
||||||
this._params = params;
|
this._params = params;
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
if (this._params.resource) {
|
if (this._params.resource) {
|
||||||
this._url = this._params.resource.url || "";
|
this._data = {
|
||||||
this._type = this._params.resource.type || undefined;
|
url: this._params.resource.url,
|
||||||
|
res_type: this._params.resource.type,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this._url = "";
|
this._data = {
|
||||||
this._type = undefined;
|
url: "",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
await this.updateComplete;
|
}
|
||||||
|
|
||||||
|
public closeDialog(): void {
|
||||||
|
this._params = undefined;
|
||||||
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._params) {
|
if (!this._params) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
const urlInvalid = this._url.trim() === "";
|
const urlInvalid = !this._data?.url || this._data.url.trim() === "";
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-dialog
|
||||||
open
|
open
|
||||||
@closed=${this._close}
|
@closed=${this.closeDialog}
|
||||||
scrimClickAction
|
scrimClickAction
|
||||||
escapeKeyAction
|
escapeKeyAction
|
||||||
.heading=${createCloseHeading(
|
.heading=${createCloseHeading(
|
||||||
@ -79,68 +81,25 @@ export class DialogLovelaceResourceDetail extends LitElement {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
|
<ha-alert
|
||||||
<div class="form">
|
alert-type="warning"
|
||||||
<h3 class="warning">
|
.title=${this.hass!.localize(
|
||||||
${this.hass!.localize(
|
|
||||||
"ui.panel.config.lovelace.resources.detail.warning_header"
|
"ui.panel.config.lovelace.resources.detail.warning_header"
|
||||||
)}
|
)}
|
||||||
</h3>
|
>
|
||||||
${this.hass!.localize(
|
${this.hass!.localize(
|
||||||
"ui.panel.config.lovelace.resources.detail.warning_text"
|
"ui.panel.config.lovelace.resources.detail.warning_text"
|
||||||
)}
|
)}
|
||||||
<paper-input
|
</ha-alert>
|
||||||
.value=${this._url}
|
|
||||||
@value-changed=${this._urlChanged}
|
<ha-form
|
||||||
.label=${this.hass!.localize(
|
.schema=${this._schema(this._data)}
|
||||||
"ui.panel.config.lovelace.resources.detail.url"
|
.data=${this._data}
|
||||||
)}
|
.hass=${this.hass}
|
||||||
.errorMessage=${this.hass!.localize(
|
.error=${this._error}
|
||||||
"ui.panel.config.lovelace.resources.detail.url_error_msg"
|
.computeLabel=${this._computeLabel}
|
||||||
)}
|
@value-changed=${this._valueChanged}
|
||||||
.invalid=${urlInvalid}
|
></ha-form>
|
||||||
dialogInitialFocus
|
|
||||||
></paper-input>
|
|
||||||
<br />
|
|
||||||
<ha-select
|
|
||||||
.label=${this.hass!.localize(
|
|
||||||
"ui.panel.config.lovelace.resources.detail.type"
|
|
||||||
)}
|
|
||||||
.value=${this._type}
|
|
||||||
@selected=${this._typeChanged}
|
|
||||||
@closed=${stopPropagation}
|
|
||||||
.invalid=${!this._type}
|
|
||||||
>
|
|
||||||
<mwc-list-item value="module">
|
|
||||||
${this.hass!.localize(
|
|
||||||
"ui.panel.config.lovelace.resources.types.module"
|
|
||||||
)}
|
|
||||||
</mwc-list-item>
|
|
||||||
${this._type === "js"
|
|
||||||
? html`
|
|
||||||
<mwc-list-item value="js">
|
|
||||||
${this.hass!.localize(
|
|
||||||
"ui.panel.config.lovelace.resources.types.js"
|
|
||||||
)}
|
|
||||||
</mwc-list-item>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
<mwc-list-item value="css">
|
|
||||||
${this.hass!.localize(
|
|
||||||
"ui.panel.config.lovelace.resources.types.css"
|
|
||||||
)}
|
|
||||||
</mwc-list-item>
|
|
||||||
${this._type === "html"
|
|
||||||
? html`
|
|
||||||
<mwc-list-item value="html">
|
|
||||||
${this.hass!.localize(
|
|
||||||
"ui.panel.config.lovelace.resources.types.html"
|
|
||||||
)}
|
|
||||||
</mwc-list-item>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
</ha-select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
${this._params.resource
|
${this._params.resource
|
||||||
? html`
|
? html`
|
||||||
@ -159,7 +118,7 @@ export class DialogLovelaceResourceDetail extends LitElement {
|
|||||||
<mwc-button
|
<mwc-button
|
||||||
slot="primaryAction"
|
slot="primaryAction"
|
||||||
@click=${this._updateResource}
|
@click=${this._updateResource}
|
||||||
.disabled=${urlInvalid || !this._type || this._submitting}
|
.disabled=${urlInvalid || !this._data?.res_type || this._submitting}
|
||||||
>
|
>
|
||||||
${this._params.resource
|
${this._params.resource
|
||||||
? this.hass!.localize(
|
? this.hass!.localize(
|
||||||
@ -173,37 +132,86 @@ export class DialogLovelaceResourceDetail extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _urlChanged(ev: PolymerChangedEvent<string>) {
|
private _schema = memoizeOne((data) => [
|
||||||
this._error = undefined;
|
{
|
||||||
this._url = ev.detail.value;
|
name: "url",
|
||||||
if (!this._type) {
|
required: true,
|
||||||
this._type = detectResourceType(this._url);
|
selector: {
|
||||||
}
|
text: {},
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "res_type",
|
||||||
|
required: true,
|
||||||
|
selector: {
|
||||||
|
select: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: "module",
|
||||||
|
label: this.hass!.localize(
|
||||||
|
"ui.panel.config.lovelace.resources.types.module"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "css",
|
||||||
|
label: this.hass!.localize(
|
||||||
|
"ui.panel.config.lovelace.resources.types.css"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
data.type === "js" && {
|
||||||
|
value: "js",
|
||||||
|
label: this.hass!.localize(
|
||||||
|
"ui.panel.config.lovelace.resources.types.js"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
data.type === "html" && {
|
||||||
|
value: "html",
|
||||||
|
label: this.hass!.localize(
|
||||||
|
"ui.panel.config.lovelace.resources.types.html"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
].filter(Boolean),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
private _typeChanged(ev) {
|
private _computeLabel = (entry: HaFormSchema): string =>
|
||||||
this._type = ev.target.value;
|
this.hass.localize(
|
||||||
|
`ui.panel.config.lovelace.resources.detail.${entry.name}`
|
||||||
|
);
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent) {
|
||||||
|
this._data = ev.detail.value;
|
||||||
|
if (!this._data!.res_type) {
|
||||||
|
const type = detectResourceType(this._data!.url);
|
||||||
|
if (!type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._data = {
|
||||||
|
...this._data,
|
||||||
|
res_type: type,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _updateResource() {
|
private async _updateResource() {
|
||||||
if (!this._type) {
|
if (!this._data?.res_type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._submitting = true;
|
this._submitting = true;
|
||||||
try {
|
try {
|
||||||
const values: LovelaceResourcesMutableParams = {
|
|
||||||
url: this._url.trim(),
|
|
||||||
res_type: this._type,
|
|
||||||
};
|
|
||||||
if (this._params!.resource) {
|
if (this._params!.resource) {
|
||||||
await this._params!.updateResource(values);
|
await this._params!.updateResource(this._data!);
|
||||||
} else {
|
} else {
|
||||||
await this._params!.createResource(values);
|
await this._params!.createResource(
|
||||||
|
this._data! as LovelaceResourcesMutableParams
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this._error = err?.message || "Unknown error";
|
this._error = { base: err?.message || "Unknown error" };
|
||||||
} finally {
|
} finally {
|
||||||
this._submitting = false;
|
this._submitting = false;
|
||||||
}
|
}
|
||||||
@ -213,26 +221,15 @@ export class DialogLovelaceResourceDetail extends LitElement {
|
|||||||
this._submitting = true;
|
this._submitting = true;
|
||||||
try {
|
try {
|
||||||
if (await this._params!.removeResource()) {
|
if (await this._params!.removeResource()) {
|
||||||
this._close();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this._submitting = false;
|
this._submitting = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _close(): void {
|
|
||||||
this._params = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return haStyleDialog;
|
||||||
haStyleDialog,
|
|
||||||
css`
|
|
||||||
.warning {
|
|
||||||
color: var(--error-color);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,6 +464,9 @@ class DialogPersonDetail extends LitElement {
|
|||||||
ha-textfield {
|
ha-textfield {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
ha-picture-upload {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
ha-formfield {
|
ha-formfield {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
|
@ -186,9 +186,7 @@ export class HaBlueprintScriptEditor extends LitElement {
|
|||||||
--paper-time-input-justify-content: flex-end;
|
--paper-time-input-justify-content: flex-end;
|
||||||
border-top: 1px solid var(--divider-color);
|
border-top: 1px solid var(--divider-color);
|
||||||
}
|
}
|
||||||
:host(:not([narrow])) ha-settings-row paper-input {
|
:host(:not([narrow])) ha-settings-row ha-textfield,
|
||||||
width: 60%;
|
|
||||||
}
|
|
||||||
:host(:not([narrow])) ha-settings-row ha-selector {
|
:host(:not([narrow])) ha-settings-row ha-selector {
|
||||||
width: 60%;
|
width: 60%;
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { addDistanceToCoord } from "../../../common/location/add_distance_to_coord";
|
import { addDistanceToCoord } from "../../../common/location/add_distance_to_coord";
|
||||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
|
||||||
import { createCloseHeading } from "../../../components/ha-dialog";
|
import { createCloseHeading } from "../../../components/ha-dialog";
|
||||||
import "../../../components/ha-formfield";
|
import "../../../components/ha-form/ha-form";
|
||||||
import "../../../components/ha-icon-picker";
|
import { HaFormSchema } from "../../../components/ha-form/types";
|
||||||
import "../../../components/ha-switch";
|
|
||||||
import "../../../components/map/ha-locations-editor";
|
|
||||||
import type { MarkerLocation } from "../../../components/map/ha-locations-editor";
|
|
||||||
import { getZoneEditorInitData, ZoneMutableParams } from "../../../data/zone";
|
import { getZoneEditorInitData, ZoneMutableParams } from "../../../data/zone";
|
||||||
import { haStyleDialog } from "../../../resources/styles";
|
import { haStyleDialog } from "../../../resources/styles";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
@ -20,19 +15,9 @@ import { ZoneDetailDialogParams } from "./show-dialog-zone-detail";
|
|||||||
class DialogZoneDetail extends LitElement {
|
class DialogZoneDetail extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@state() private _name!: string;
|
@state() private _error?: Record<string, string>;
|
||||||
|
|
||||||
@state() private _icon!: string;
|
@state() private _data?: ZoneMutableParams;
|
||||||
|
|
||||||
@state() private _latitude!: number;
|
|
||||||
|
|
||||||
@state() private _longitude!: number;
|
|
||||||
|
|
||||||
@state() private _passive!: boolean;
|
|
||||||
|
|
||||||
@state() private _radius!: number;
|
|
||||||
|
|
||||||
@state() private _error?: string;
|
|
||||||
|
|
||||||
@state() private _params?: ZoneDetailDialogParams;
|
@state() private _params?: ZoneDetailDialogParams;
|
||||||
|
|
||||||
@ -42,13 +27,7 @@ class DialogZoneDetail extends LitElement {
|
|||||||
this._params = params;
|
this._params = params;
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
if (this._params.entry) {
|
if (this._params.entry) {
|
||||||
this._name = this._params.entry.name || "";
|
this._data = this._params.entry;
|
||||||
this._icon = this._params.entry.icon || "";
|
|
||||||
this._latitude = this._params.entry.latitude || this.hass.config.latitude;
|
|
||||||
this._longitude =
|
|
||||||
this._params.entry.longitude || this.hass.config.longitude;
|
|
||||||
this._passive = this._params.entry.passive || false;
|
|
||||||
this._radius = this._params.entry.radius || 100;
|
|
||||||
} else {
|
} else {
|
||||||
const initConfig = getZoneEditorInitData();
|
const initConfig = getZoneEditorInitData();
|
||||||
let movedHomeLocation;
|
let movedHomeLocation;
|
||||||
@ -59,30 +38,34 @@ class DialogZoneDetail extends LitElement {
|
|||||||
Math.random() * 500 * (Math.random() < 0.5 ? -1 : 1)
|
Math.random() * 500 * (Math.random() < 0.5 ? -1 : 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this._latitude = initConfig?.latitude || movedHomeLocation[0];
|
this._data = {
|
||||||
this._longitude = initConfig?.longitude || movedHomeLocation[1];
|
latitude: initConfig?.latitude || movedHomeLocation[0],
|
||||||
this._name = initConfig?.name || "";
|
longitude: initConfig?.longitude || movedHomeLocation[1],
|
||||||
this._icon = initConfig?.icon || "mdi:map-marker";
|
name: initConfig?.name || "",
|
||||||
|
icon: initConfig?.icon || "mdi:map-marker",
|
||||||
this._passive = false;
|
passive: false,
|
||||||
this._radius = 100;
|
radius: 100,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeDialog(): void {
|
public closeDialog(): void {
|
||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
|
this._data = undefined;
|
||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._params) {
|
if (!this._params || !this._data) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
const nameInvalid = this._name.trim() === "";
|
const nameInvalid = this._data.name.trim() === "";
|
||||||
const iconInvalid = Boolean(this._icon && !this._icon.trim().includes(":"));
|
const iconInvalid = Boolean(
|
||||||
const latInvalid = String(this._latitude) === "";
|
this._data.icon && !this._data.icon.trim().includes(":")
|
||||||
const lngInvalid = String(this._longitude) === "";
|
);
|
||||||
const radiusInvalid = String(this._radius) === "";
|
const latInvalid = String(this._data.latitude) === "";
|
||||||
|
const lngInvalid = String(this._data.longitude) === "";
|
||||||
|
const radiusInvalid = String(this._data.radius) === "";
|
||||||
|
|
||||||
const valid =
|
const valid =
|
||||||
!nameInvalid &&
|
!nameInvalid &&
|
||||||
@ -105,96 +88,15 @@ class DialogZoneDetail extends LitElement {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
|
<ha-form
|
||||||
<div class="form">
|
|
||||||
<paper-input
|
|
||||||
dialogInitialFocus
|
|
||||||
.value=${this._name}
|
|
||||||
.configValue=${"name"}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
.label=${this.hass!.localize("ui.panel.config.zone.detail.name")}
|
|
||||||
.errorMessage=${this.hass!.localize(
|
|
||||||
"ui.panel.config.zone.detail.required_error_msg"
|
|
||||||
)}
|
|
||||||
required
|
|
||||||
auto-validate
|
|
||||||
></paper-input>
|
|
||||||
<ha-icon-picker
|
|
||||||
.value=${this._icon}
|
|
||||||
.configValue=${"icon"}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
.label=${this.hass!.localize("ui.panel.config.zone.detail.icon")}
|
|
||||||
.errorMessage=${this.hass!.localize(
|
|
||||||
"ui.panel.config.zone.detail.icon_error_msg"
|
|
||||||
)}
|
|
||||||
.invalid=${iconInvalid}
|
|
||||||
></ha-icon-picker>
|
|
||||||
<ha-locations-editor
|
|
||||||
class="flex"
|
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.locations=${this._location(
|
.schema=${this._schema(this._data.icon)}
|
||||||
this._latitude,
|
.data=${this._formData(this._data)}
|
||||||
this._longitude,
|
.error=${this._error}
|
||||||
this._radius,
|
.computeLabel=${this._computeLabel}
|
||||||
this._passive,
|
class=${this._data.passive ? "passive" : ""}
|
||||||
this._icon
|
|
||||||
)}
|
|
||||||
@location-updated=${this._locationChanged}
|
|
||||||
@radius-updated=${this._radiusChanged}
|
|
||||||
></ha-locations-editor>
|
|
||||||
<div class="location">
|
|
||||||
<paper-input
|
|
||||||
.value=${this._latitude}
|
|
||||||
.configValue=${"latitude"}
|
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
.label=${this.hass!.localize(
|
></ha-form>
|
||||||
"ui.panel.config.zone.detail.latitude"
|
|
||||||
)}
|
|
||||||
.errorMessage=${this.hass!.localize(
|
|
||||||
"ui.panel.config.zone.detail.required_error_msg"
|
|
||||||
)}
|
|
||||||
.invalid=${latInvalid}
|
|
||||||
></paper-input>
|
|
||||||
<paper-input
|
|
||||||
.value=${this._longitude}
|
|
||||||
.configValue=${"longitude"}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
.label=${this.hass!.localize(
|
|
||||||
"ui.panel.config.zone.detail.longitude"
|
|
||||||
)}
|
|
||||||
.errorMessage=${this.hass!.localize(
|
|
||||||
"ui.panel.config.zone.detail.required_error_msg"
|
|
||||||
)}
|
|
||||||
.invalid=${lngInvalid}
|
|
||||||
></paper-input>
|
|
||||||
</div>
|
|
||||||
<paper-input
|
|
||||||
.value=${this._radius}
|
|
||||||
.configValue=${"radius"}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
.label=${this.hass!.localize(
|
|
||||||
"ui.panel.config.zone.detail.radius"
|
|
||||||
)}
|
|
||||||
.errorMessage=${this.hass!.localize(
|
|
||||||
"ui.panel.config.zone.detail.required_error_msg"
|
|
||||||
)}
|
|
||||||
.invalid=${radiusInvalid}
|
|
||||||
></paper-input>
|
|
||||||
<p>
|
|
||||||
${this.hass!.localize("ui.panel.config.zone.detail.passive_note")}
|
|
||||||
</p>
|
|
||||||
<ha-formfield
|
|
||||||
.label=${this.hass!.localize(
|
|
||||||
"ui.panel.config.zone.detail.passive"
|
|
||||||
)}
|
|
||||||
.dir=${computeRTLDirection(this.hass)}
|
|
||||||
>
|
|
||||||
<ha-switch
|
|
||||||
.checked=${this._passive}
|
|
||||||
@change=${this._passiveChanged}
|
|
||||||
></ha-switch>
|
|
||||||
</ha-formfield>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
${this._params.entry
|
${this._params.entry
|
||||||
? html`
|
? html`
|
||||||
@ -221,74 +123,94 @@ class DialogZoneDetail extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _location = memoizeOne(
|
private _schema = memoizeOne((icon?: string): HaFormSchema[] => [
|
||||||
(
|
|
||||||
lat: number,
|
|
||||||
lng: number,
|
|
||||||
radius: number,
|
|
||||||
passive: boolean,
|
|
||||||
icon: string
|
|
||||||
): MarkerLocation[] => {
|
|
||||||
const computedStyles = getComputedStyle(this);
|
|
||||||
const zoneRadiusColor = computedStyles.getPropertyValue("--accent-color");
|
|
||||||
const passiveRadiusColor = computedStyles.getPropertyValue(
|
|
||||||
"--secondary-text-color"
|
|
||||||
);
|
|
||||||
return [
|
|
||||||
{
|
{
|
||||||
id: "location",
|
name: "name",
|
||||||
latitude: Number(lat),
|
required: true,
|
||||||
longitude: Number(lng),
|
selector: {
|
||||||
radius,
|
text: {},
|
||||||
radius_color: passive ? passiveRadiusColor : zoneRadiusColor,
|
|
||||||
icon,
|
|
||||||
location_editable: true,
|
|
||||||
radius_editable: true,
|
|
||||||
},
|
},
|
||||||
];
|
},
|
||||||
}
|
{
|
||||||
);
|
name: "icon",
|
||||||
|
required: false,
|
||||||
|
selector: {
|
||||||
|
icon: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "location",
|
||||||
|
required: true,
|
||||||
|
selector: { location: { radius: true, icon } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "",
|
||||||
|
type: "grid",
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
name: "latitude",
|
||||||
|
required: true,
|
||||||
|
selector: { text: {} },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "longitude",
|
||||||
|
required: true,
|
||||||
|
|
||||||
private _locationChanged(ev: CustomEvent) {
|
selector: { text: {} },
|
||||||
[this._latitude, this._longitude] = ev.detail.location;
|
},
|
||||||
}
|
],
|
||||||
|
},
|
||||||
|
{ name: "passive_note", type: "constant" },
|
||||||
|
{ name: "passive", selector: { boolean: {} } },
|
||||||
|
{
|
||||||
|
name: "radius",
|
||||||
|
required: false,
|
||||||
|
selector: { number: { min: 0, max: 999999, mode: "box" } },
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
private _radiusChanged(ev: CustomEvent) {
|
private _formData = memoizeOne((data: ZoneMutableParams) => ({
|
||||||
this._radius = ev.detail.radius;
|
...data,
|
||||||
}
|
location: {
|
||||||
|
latitude: data.latitude,
|
||||||
private _passiveChanged(ev) {
|
longitude: data.longitude,
|
||||||
this._passive = ev.target.checked;
|
radius: data.radius,
|
||||||
}
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
private _valueChanged(ev: CustomEvent) {
|
private _valueChanged(ev: CustomEvent) {
|
||||||
const configValue = (ev.target as any).configValue;
|
|
||||||
|
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this[`_${configValue}`] = ev.detail.value;
|
const value = ev.detail.value;
|
||||||
|
if (
|
||||||
|
value.location.latitude !== this._data!.latitude ||
|
||||||
|
value.location.longitude !== this._data!.longitude ||
|
||||||
|
value.location.radius !== this._data!.radius
|
||||||
|
) {
|
||||||
|
value.latitude = value.location.latitude;
|
||||||
|
value.longitude = value.location.longitude;
|
||||||
|
value.radius = Math.round(value.location.radius);
|
||||||
}
|
}
|
||||||
|
delete value.location;
|
||||||
|
if (!value.icon) {
|
||||||
|
delete value.icon;
|
||||||
|
}
|
||||||
|
this._data = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _computeLabel = (entry: HaFormSchema): string =>
|
||||||
|
this.hass.localize(`ui.panel.config.zone.detail.${entry.name}`);
|
||||||
|
|
||||||
private async _updateEntry() {
|
private async _updateEntry() {
|
||||||
this._submitting = true;
|
this._submitting = true;
|
||||||
try {
|
try {
|
||||||
const values: ZoneMutableParams = {
|
|
||||||
name: this._name.trim(),
|
|
||||||
latitude: this._latitude,
|
|
||||||
longitude: this._longitude,
|
|
||||||
passive: this._passive,
|
|
||||||
radius: this._radius,
|
|
||||||
};
|
|
||||||
if (this._icon) {
|
|
||||||
values.icon = this._icon.trim();
|
|
||||||
}
|
|
||||||
if (this._params!.entry) {
|
if (this._params!.entry) {
|
||||||
await this._params!.updateEntry!(values);
|
await this._params!.updateEntry!(this._data!);
|
||||||
} else {
|
} else {
|
||||||
await this._params!.createEntry(values);
|
await this._params!.createEntry(this._data!);
|
||||||
}
|
}
|
||||||
this._params = undefined;
|
this.closeDialog();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this._error = err ? err.message : "Unknown error";
|
this._error = { base: err ? err.message : "Unknown error" };
|
||||||
} finally {
|
} finally {
|
||||||
this._submitting = false;
|
this._submitting = false;
|
||||||
}
|
}
|
||||||
@ -309,24 +231,18 @@ class DialogZoneDetail extends LitElement {
|
|||||||
return [
|
return [
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
.location {
|
ha-dialog {
|
||||||
display: flex;
|
--mdc-dialog-min-width: 600px;
|
||||||
}
|
}
|
||||||
.location > * {
|
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||||
flex-grow: 1;
|
ha-dialog {
|
||||||
min-width: 0;
|
--mdc-dialog-min-width: calc(
|
||||||
|
100vw - env(safe-area-inset-right) - env(safe-area-inset-left)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
.location > *:first-child {
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
}
|
||||||
.location > *:last-child {
|
ha-form.passive {
|
||||||
margin-left: 4px;
|
--zone-radius-color: var(--secondary-text-color);
|
||||||
}
|
|
||||||
ha-locations-editor {
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { mdiClose } from "@mdi/js";
|
import { mdiClose } from "@mdi/js";
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { EditorTarget } from "../editor/types";
|
import { EditorTarget } from "../editor/types";
|
||||||
|
import "../../../components/ha-textfield";
|
||||||
|
|
||||||
@customElement("hui-input-list-editor")
|
@customElement("hui-input-list-editor")
|
||||||
export class HuiInputListEditor extends LitElement {
|
export class HuiInputListEditor extends LitElement {
|
||||||
@ -23,30 +23,31 @@ export class HuiInputListEditor extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
${this.value.map(
|
${this.value.map(
|
||||||
(listEntry, index) => html`
|
(listEntry, index) => html`
|
||||||
<paper-input
|
<ha-textfield
|
||||||
label=${this.inputLabel}
|
.label=${this.inputLabel}
|
||||||
.value=${listEntry}
|
.value=${listEntry}
|
||||||
.configValue=${"entry"}
|
.configValue=${"entry"}
|
||||||
.index=${index}
|
.index=${index}
|
||||||
@value-changed=${this._valueChanged}
|
@input=${this._valueChanged}
|
||||||
@blur=${this._consolidateEntries}
|
@blur=${this._consolidateEntries}
|
||||||
@keydown=${this._handleKeyDown}
|
@keydown=${this._handleKeyDown}
|
||||||
|
iconTrailing
|
||||||
><ha-icon-button
|
><ha-icon-button
|
||||||
slot="suffix"
|
slot="trailingIcon"
|
||||||
class="clear-button"
|
class="clear-button"
|
||||||
.path=${mdiClose}
|
.path=${mdiClose}
|
||||||
no-ripple
|
no-ripple
|
||||||
@click=${this._removeEntry}
|
@click=${this._removeEntry}
|
||||||
.label=${this.hass!.localize("ui.common.clear")}
|
.label=${this.hass!.localize("ui.common.clear")}
|
||||||
>Clear</ha-icon-button
|
|
||||||
></paper-input
|
|
||||||
>
|
>
|
||||||
|
</ha-icon-button>
|
||||||
|
</ha-textfield>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
<paper-input
|
<ha-textfield
|
||||||
label=${this.inputLabel}
|
.label=${this.inputLabel}
|
||||||
@change=${this._addEntry}
|
@change=${this._addEntry}
|
||||||
></paper-input>
|
></ha-textfield>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,10 +104,12 @@ export class HuiInputListEditor extends LitElement {
|
|||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
ha-icon-button {
|
ha-icon-button {
|
||||||
--mdc-icon-button-size: 24px;
|
margin-right: -24px;
|
||||||
padding: 2px;
|
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
ha-textfield {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import {
|
import {
|
||||||
@ -24,7 +23,7 @@ import { EntityConfig } from "../../entity-rows/types";
|
|||||||
import { LovelaceCardEditor } from "../../types";
|
import { LovelaceCardEditor } from "../../types";
|
||||||
import { processEditorEntities } from "../process-editor-entities";
|
import { processEditorEntities } from "../process-editor-entities";
|
||||||
import { entitiesConfigStruct } from "../structs/entities-struct";
|
import { entitiesConfigStruct } from "../structs/entities-struct";
|
||||||
import { EditorTarget, EntitiesEditorEvent } from "../types";
|
import { EntitiesEditorEvent } from "../types";
|
||||||
import { configElementStyle } from "./config-elements-style";
|
import { configElementStyle } from "./config-elements-style";
|
||||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||||
import { HaFormSchema } from "../../../../components/ha-form/types";
|
import { HaFormSchema } from "../../../../components/ha-form/types";
|
||||||
@ -127,10 +126,6 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
|||||||
if (!this._config || !this.hass) {
|
if (!this._config || !this.hass) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const target = ev.target! as EditorTarget;
|
|
||||||
if (!target.configValue) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = ev.detail.value;
|
const value = ev.detail.value;
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { assert, object, optional, string, assign } from "superstruct";
|
import { assert, object, optional, string, assign } from "superstruct";
|
||||||
|
@ -153,7 +153,7 @@ export class HuiStatisticsGraphCardEditor
|
|||||||
.pickedStatisticLabel=${`Statistic`}
|
.pickedStatisticLabel=${`Statistic`}
|
||||||
.value=${this._configEntities}
|
.value=${this._configEntities}
|
||||||
.configValue=${"entities"}
|
.configValue=${"entities"}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._entitiesChanged}
|
||||||
></ha-statistics-picker>
|
></ha-statistics-picker>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@ -163,6 +163,12 @@ export class HuiStatisticsGraphCardEditor
|
|||||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _entitiesChanged(ev: CustomEvent): void {
|
||||||
|
fireEvent(this, "config-changed", {
|
||||||
|
config: { ...this._config!, entities: ev.detail.value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _computeLabelCallback = (schema: HaFormSchema) =>
|
private _computeLabelCallback = (schema: HaFormSchema) =>
|
||||||
this.hass!.localize(
|
this.hass!.localize(
|
||||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||||
|
@ -87,6 +87,7 @@ class HuiInputSelectEntityRow extends LitElement implements LovelaceRow {
|
|||||||
}
|
}
|
||||||
ha-select {
|
ha-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
--ha-select-min-width: 0;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -2605,6 +2605,7 @@
|
|||||||
"finish": "Finish",
|
"finish": "Finish",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"next": "Next",
|
"next": "Next",
|
||||||
|
"no_config_flow": "This integration does not support configuration via the UI. If you followed this link from the Home Assistant website, make sure you run the latest version of Home Assistant.",
|
||||||
"not_all_required_fields": "Not all required fields are filled in.",
|
"not_all_required_fields": "Not all required fields are filled in.",
|
||||||
"error_saving_area": "Error saving area: {error}",
|
"error_saving_area": "Error saving area: {error}",
|
||||||
"created_config": "Created configuration for {name}.",
|
"created_config": "Created configuration for {name}.",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user