20231205.0 (#18916)

This commit is contained in:
Bram Kragten 2023-12-05 18:10:37 +01:00 committed by GitHub
commit eb5e7ba3f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
114 changed files with 1631 additions and 853 deletions

View File

@ -39,7 +39,9 @@ export class HADemoCard extends LitElement implements LovelaceCard {
<div class="picker"> <div class="picker">
<div class="label"> <div class="label">
${this._switching ${this._switching
? html`<ha-circular-progress active></ha-circular-progress>` ? html`<ha-circular-progress
indeterminate
></ha-circular-progress>`
: until( : until(
selectedDemoConfig.then( selectedDemoConfig.then(
(conf) => html` (conf) => html`

View File

@ -0,0 +1,4 @@
---
title: Circular Progress
subtitle: Can be used to indicate an ongoing task.
---

View File

@ -0,0 +1,64 @@
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../src/components/ha-bar";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-circular-progress";
import "@material/web/progress/circular-progress";
import { HomeAssistant } from "../../../../src/types";
@customElement("demo-components-ha-circular-progress")
export class DemoHaCircularProgress extends LitElement {
@property({ attribute: false }) hass!: HomeAssistant;
protected render(): TemplateResult {
return html`<ha-card header="Basic circular progress">
<div class="card-content">
<ha-circular-progress indeterminate></ha-circular-progress></div
></ha-card>
<ha-card header="Different circular progress sizes">
<div class="card-content">
<ha-circular-progress
indeterminate
size="tiny"
></ha-circular-progress>
<ha-circular-progress
indeterminate
size="small"
></ha-circular-progress>
<ha-circular-progress
indeterminate
size="medium"
></ha-circular-progress>
<ha-circular-progress
indeterminate
size="large"
></ha-circular-progress></div
></ha-card>
<ha-card header="Circular progress with an aria-label">
<div class="card-content">
<ha-circular-progress
indeterminate
aria-label="Doing something..."
></ha-circular-progress>
<ha-circular-progress
indeterminate
.ariaLabel=${"Doing something..."}
></ha-circular-progress></div
></ha-card>`;
}
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-components-ha-circular-progress": DemoHaCircularProgress;
}
}

View File

@ -20,7 +20,7 @@ class HassioAddonConfigDashboard extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.addon) { if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`; return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
} }
const hasConfiguration = const hasConfiguration =
(this.addon.options && Object.keys(this.addon.options).length) || (this.addon.options && Object.keys(this.addon.options).length) ||

View File

@ -34,7 +34,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.addon) { if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`; return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
} }
return html` return html`
<div class="content"> <div class="content">

View File

@ -22,7 +22,7 @@ class HassioAddonInfoDashboard extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.addon) { if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`; return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
} }
return html` return html`

View File

@ -18,7 +18,9 @@ class HassioAddonLogDashboard extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.addon) { if (!this.addon) {
return html` <ha-circular-progress active></ha-circular-progress> `; return html`
<ha-circular-progress indeterminate></ha-circular-progress>
`;
} }
return html` return html`
<div class="content"> <div class="content">

View File

@ -95,7 +95,7 @@ class HassioBackupDialog
</ha-header-bar> </ha-header-bar>
</div> </div>
${this._restoringBackup ${this._restoringBackup
? html`<ha-circular-progress active></ha-circular-progress>` ? html`<ha-circular-progress indeterminate></ha-circular-progress>`
: html` : html`
<supervisor-backup-content <supervisor-backup-content
.hass=${this.hass} .hass=${this.hass}

View File

@ -57,7 +57,7 @@ class HassioCreateBackupDialog extends LitElement {
)} )}
> >
${this._creatingBackup ${this._creatingBackup
? html`<ha-circular-progress active></ha-circular-progress>` ? html`<ha-circular-progress indeterminate></ha-circular-progress>`
: html`<supervisor-backup-content : html`<supervisor-backup-content
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this._dialogParams.supervisor} .supervisor=${this._dialogParams.supervisor}

View File

@ -71,7 +71,11 @@ class HassioDatadiskDialog extends LitElement {
?hideActions=${this.moving} ?hideActions=${this.moving}
> >
${this.moving ${this.moving
? html` <ha-circular-progress alt="Moving" size="large" active> ? html` <ha-circular-progress
aria-label="Moving"
size="large"
indeterminate
>
</ha-circular-progress> </ha-circular-progress>
<p class="progress-text"> <p class="progress-text">
${this.dialogParams.supervisor.localize( ${this.dialogParams.supervisor.localize(

View File

@ -155,7 +155,11 @@ export class DialogHassioNetwork
.disabled=${this._scanning} .disabled=${this._scanning}
> >
${this._scanning ${this._scanning
? html`<ha-circular-progress active size="small"> ? html`<ha-circular-progress
aria-label="Scanning"
indeterminate
size="small"
>
</ha-circular-progress>` </ha-circular-progress>`
: this.supervisor.localize("dialog.network.scan_ap")} : this.supervisor.localize("dialog.network.scan_ap")}
</mwc-button> </mwc-button>
@ -274,7 +278,7 @@ export class DialogHassioNetwork
</mwc-button> </mwc-button>
<mwc-button @click=${this._updateNetwork} .disabled=${!this._dirty}> <mwc-button @click=${this._updateNetwork} .disabled=${!this._dirty}>
${this._processing ${this._processing
? html`<ha-circular-progress active size="small"> ? html`<ha-circular-progress indeterminate size="small">
</ha-circular-progress>` </ha-circular-progress>`
: this.supervisor.localize("common.save")} : this.supervisor.localize("common.save")}
</mwc-button> </mwc-button>

View File

@ -158,7 +158,7 @@ class HassioRepositoriesDialog extends LitElement {
<mwc-button @click=${this._addRepository}> <mwc-button @click=${this._addRepository}>
${this._processing ${this._processing
? html`<ha-circular-progress ? html`<ha-circular-progress
active indeterminate
size="small" size="small"
></ha-circular-progress>` ></ha-circular-progress>`
: this._dialogParams!.supervisor.localize( : this._dialogParams!.supervisor.localize(

View File

@ -174,7 +174,11 @@ class UpdateAvailableCard extends LitElement {
` `
: ""} : ""}
` `
: html`<ha-circular-progress alt="Updating" size="large" active> : html`<ha-circular-progress
aria-label="Updating"
size="large"
indeterminate
>
</ha-circular-progress> </ha-circular-progress>
<p class="progress-text"> <p class="progress-text">
${this.supervisor.localize("update_available.updating", { ${this.supervisor.localize("update_available.updating", {

View File

@ -60,7 +60,6 @@
"@material/mwc-base": "0.27.0", "@material/mwc-base": "0.27.0",
"@material/mwc-button": "0.27.0", "@material/mwc-button": "0.27.0",
"@material/mwc-checkbox": "0.27.0", "@material/mwc-checkbox": "0.27.0",
"@material/mwc-circular-progress": "0.27.0",
"@material/mwc-dialog": "0.27.0", "@material/mwc-dialog": "0.27.0",
"@material/mwc-drawer": "0.27.0", "@material/mwc-drawer": "0.27.0",
"@material/mwc-fab": "0.27.0", "@material/mwc-fab": "0.27.0",
@ -91,8 +90,8 @@
"@polymer/paper-toast": "3.0.1", "@polymer/paper-toast": "3.0.1",
"@polymer/polymer": "3.5.1", "@polymer/polymer": "3.5.1",
"@thomasloven/round-slider": "0.6.0", "@thomasloven/round-slider": "0.6.0",
"@vaadin/combo-box": "24.2.4", "@vaadin/combo-box": "24.2.5",
"@vaadin/vaadin-themable-mixin": "24.2.4", "@vaadin/vaadin-themable-mixin": "24.2.5",
"@vibrant/color": "3.2.1-alpha.1", "@vibrant/color": "3.2.1-alpha.1",
"@vibrant/core": "3.2.1-alpha.1", "@vibrant/core": "3.2.1-alpha.1",
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1", "@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
@ -120,14 +119,13 @@
"leaflet-draw": "1.0.4", "leaflet-draw": "1.0.4",
"lit": "2.8.0", "lit": "2.8.0",
"luxon": "3.4.4", "luxon": "3.4.4",
"marked": "10.0.0", "marked": "11.0.0",
"memoize-one": "6.0.0", "memoize-one": "6.0.0",
"node-vibrant": "3.2.1-alpha.1", "node-vibrant": "3.2.1-alpha.1",
"proxy-polyfill": "0.3.2", "proxy-polyfill": "0.3.2",
"punycode": "2.3.1", "punycode": "2.3.1",
"qr-scanner": "1.4.2", "qr-scanner": "1.4.2",
"qrcode": "1.5.3", "qrcode": "1.5.3",
"resize-observer-polyfill": "1.5.1",
"roboto-fontface": "0.10.0", "roboto-fontface": "0.10.0",
"rrule": "2.8.1", "rrule": "2.8.1",
"sortablejs": "1.15.1", "sortablejs": "1.15.1",
@ -194,7 +192,7 @@
"babel-plugin-template-html-minifier": "4.1.0", "babel-plugin-template-html-minifier": "4.1.0",
"chai": "4.3.10", "chai": "4.3.10",
"del": "7.1.0", "del": "7.1.0",
"eslint": "8.54.0", "eslint": "8.55.0",
"eslint-config-airbnb-base": "15.0.0", "eslint-config-airbnb-base": "15.0.0",
"eslint-config-airbnb-typescript": "17.1.0", "eslint-config-airbnb-typescript": "17.1.0",
"eslint-config-prettier": "9.0.0", "eslint-config-prettier": "9.0.0",
@ -206,7 +204,7 @@
"eslint-plugin-unused-imports": "3.0.0", "eslint-plugin-unused-imports": "3.0.0",
"eslint-plugin-wc": "2.0.4", "eslint-plugin-wc": "2.0.4",
"fancy-log": "2.0.0", "fancy-log": "2.0.0",
"fs-extra": "11.1.1", "fs-extra": "11.2.0",
"glob": "10.3.10", "glob": "10.3.10",
"gulp": "4.0.2", "gulp": "4.0.2",
"gulp-flatmap": "1.0.2", "gulp-flatmap": "1.0.2",

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "home-assistant-frontend" name = "home-assistant-frontend"
version = "20231204.0" version = "20231205.0"
license = {text = "Apache-2.0"} license = {text = "Apache-2.0"}
description = "The Home Assistant frontend" description = "The Home Assistant frontend"
readme = "README.md" readme = "README.md"

View File

@ -45,7 +45,7 @@ export class HaProgressButton extends LitElement {
? html` ? html`
<ha-circular-progress <ha-circular-progress
size="small" size="small"
active indeterminate
></ha-circular-progress> ></ha-circular-progress>
` `
: ""} : ""}

View File

@ -469,6 +469,7 @@ export class HaChartBase extends LitElement {
.chartTooltip li { .chartTooltip li {
display: flex; display: flex;
white-space: pre-line; white-space: pre-line;
word-break: break-word;
align-items: center; align-items: center;
line-height: 16px; line-height: 16px;
padding: 4px 0; padding: 4px 0;
@ -476,6 +477,7 @@ export class HaChartBase extends LitElement {
.chartTooltip .title { .chartTooltip .title {
text-align: center; text-align: center;
font-weight: 500; font-weight: 500;
word-break: break-word;
direction: ltr; direction: ltr;
} }
.chartTooltip .footer { .chartTooltip .footer {

View File

@ -1,54 +1,44 @@
import { CircularProgress } from "@material/mwc-circular-progress"; import "element-internals-polyfill";
import { CSSResultGroup, css } from "lit"; import { MdCircularProgress } from "@material/web/progress/circular-progress";
import { CSSResult, PropertyValues, css } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
@customElement("ha-circular-progress") @customElement("ha-circular-progress")
// @ts-ignore export class HaCircularProgress extends MdCircularProgress {
export class HaCircularProgress extends CircularProgress { @property({ attribute: "aria-label", type: String }) public ariaLabel =
@property({ type: Boolean }) "Loading";
public active = false;
@property() @property() public size: "tiny" | "small" | "medium" | "large" = "medium";
public alt = "Loading";
@property() protected updated(changedProps: PropertyValues) {
public size: "tiny" | "small" | "medium" | "large" = "medium"; super.updated(changedProps);
// @ts-ignore if (changedProps.has("size")) {
public set density(_) { switch (this.size) {
// just a dummy case "tiny":
} this.style.setProperty("--md-circular-progress-size", "16px");
break;
public get density() { case "small":
switch (this.size) { this.style.setProperty("--md-circular-progress-size", "28px");
case "tiny": break;
return -8; // medium is default size
case "small": case "medium":
return -5; this.style.setProperty("--md-circular-progress-size", "48px");
case "medium": break;
return 0; case "large":
case "large": this.style.setProperty("--md-circular-progress-size", "68px");
return 5; break;
default: }
return 0;
} }
} }
// @ts-ignore static get styles(): CSSResult[] {
public set indeterminate(_) {
// just a dummy
}
public get indeterminate() {
return this.active;
}
static get styles(): CSSResultGroup {
return [ return [
super.styles, ...super.styles,
css` css`
:host { :host {
overflow: hidden; --md-sys-color-primary: var(--primary-color);
--md-circular-progress-size: 48px;
} }
`, `,
]; ];

View File

@ -618,6 +618,7 @@ export class HaControlCircularSlider extends LitElement {
--control-circular-slider-high-color: var( --control-circular-slider-high-color: var(
--control-circular-slider-color --control-circular-slider-color
); );
--control-circular-slider-interaction-margin: 12px;
width: 320px; width: 320px;
display: block; display: block;
} }
@ -633,7 +634,9 @@ export class HaControlCircularSlider extends LitElement {
fill: none; fill: none;
stroke: transparent; stroke: transparent;
stroke-linecap: round; stroke-linecap: round;
stroke-width: 48px; stroke-width: calc(
24px + 2 * var(--control-circular-slider-interaction-margin)
);
cursor: pointer; cursor: pointer;
} }
#display { #display {

View File

@ -1,3 +1,4 @@
import { ResizeController } from "@lit-labs/observers/resize-controller";
import { mdiMinus, mdiPlus } from "@mdi/js"; import { mdiMinus, mdiPlus } from "@mdi/js";
import { import {
CSSResultGroup, CSSResultGroup,
@ -49,6 +50,13 @@ export class HaControlNumberButton extends LitElement {
@query("#input") _input!: HTMLDivElement; @query("#input") _input!: HTMLDivElement;
private _hideUnit = new ResizeController(this, {
callback: (entries) => {
const width = entries[0]?.contentRect.width;
return width < 100;
},
});
private boundedValue(value: number) { private boundedValue(value: number) {
const clamped = conditionalClamp(value, this.min, this.max); const clamped = conditionalClamp(value, this.min, this.max);
return Math.round(clamped / this._step) * this._step; return Math.round(clamped / this._step) * this._step;
@ -145,7 +153,10 @@ export class HaControlNumberButton extends LitElement {
?disabled=${this.disabled} ?disabled=${this.disabled}
@keydown=${this._handleKeyDown} @keydown=${this._handleKeyDown}
> >
${value} ${unit ? html`<span class="unit">${unit}</span>` : nothing} ${value}
${unit && !this._hideUnit.value
? html`<span class="unit">${unit}</span>`
: nothing}
</div> </div>
<button <button
class="button minus" class="button minus"

View File

@ -6,6 +6,11 @@ import { MdOutlinedIconButton } from "@material/web/iconbutton/outlined-icon-but
@customElement("ha-outlined-icon-button") @customElement("ha-outlined-icon-button")
export class HaOutlinedIconButton extends MdOutlinedIconButton { export class HaOutlinedIconButton extends MdOutlinedIconButton {
static override styles = [ static override styles = [
css`
.icon-button {
border-radius: var(--_container-shape);
}
`,
...super.styles, ...super.styles,
css` css`
:host { :host {

View File

@ -1,9 +1,19 @@
import { DEFAULT_SCHEMA, dump, load, Schema } from "js-yaml"; import { DEFAULT_SCHEMA, dump, load, Schema } from "js-yaml";
import { html, LitElement, nothing, PropertyValues } from "lit"; import {
CSSResultGroup,
css,
html,
LitElement,
nothing,
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 type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { haStyle } from "../resources/styles";
import "./ha-code-editor"; import "./ha-code-editor";
import { showToast } from "../util/toast";
import { copyToClipboard } from "../common/util/copy-clipboard";
const isEmpty = (obj: Record<string, unknown>): boolean => { const isEmpty = (obj: Record<string, unknown>): boolean => {
if (typeof obj !== "object") { if (typeof obj !== "object") {
@ -37,6 +47,8 @@ export class HaYamlEditor extends LitElement {
@property({ type: Boolean }) public required = false; @property({ type: Boolean }) public required = false;
@property({ type: Boolean }) public copyClipboard = false;
@state() private _yaml = ""; @state() private _yaml = "";
public setValue(value): void { public setValue(value): void {
@ -88,6 +100,15 @@ export class HaYamlEditor extends LitElement {
@value-changed=${this._onChange} @value-changed=${this._onChange}
dir="ltr" dir="ltr"
></ha-code-editor> ></ha-code-editor>
${this.copyClipboard
? html`<div class="card-actions">
<mwc-button @click=${this._copyYaml}>
${this.hass.localize(
"ui.components.yaml-editor.copy_to_clipboard"
)}
</mwc-button>
</div>`
: nothing}
`; `;
} }
@ -117,6 +138,35 @@ export class HaYamlEditor extends LitElement {
get yaml() { get yaml() {
return this._yaml; return this._yaml;
} }
private async _copyYaml(): Promise<void> {
if (this.yaml) {
await copyToClipboard(this.yaml);
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),
});
}
}
static get styles(): CSSResultGroup {
return [
haStyle,
css`
.card-actions {
border-radius: var(
--actions-border-radius,
0px 0px var(--ha-card-border-radius, 12px)
var(--ha-card-border-radius, 12px)
);
border: 1px solid var(--divider-color);
padding: 5px 16px;
}
ha-code-editor {
flex-grow: 1;
}
`,
];
}
} }
declare global { declare global {

View File

@ -147,7 +147,7 @@ class DialogMediaManage extends LitElement {
${!this._currentItem ${!this._currentItem
? html` ? html`
<div class="refresh"> <div class="refresh">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
</div> </div>
` `
: !children.length : !children.length

View File

@ -332,7 +332,7 @@ export class HaMediaPlayerBrowse extends LitElement {
} }
if (!this._currentItem) { if (!this._currentItem) {
return html`<ha-circular-progress active></ha-circular-progress>`; return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
} }
const currentItem = this._currentItem; const currentItem = this._currentItem;

View File

@ -54,8 +54,8 @@ class MediaUploadButton extends LitElement {
? html` ? html`
<ha-circular-progress <ha-circular-progress
size="tiny" size="tiny"
active indeterminate
alt="" area-label="Uploading"
slot="icon" slot="icon"
></ha-circular-progress> ></ha-circular-progress>
` `

View File

@ -2,6 +2,7 @@ import {
HassEntityAttributeBase, HassEntityAttributeBase,
HassEntityBase, HassEntityBase,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { getExtendedEntityRegistryEntry } from "./entity_registry";
import { showEnterCodeDialogDialog } from "../dialogs/enter-code/show-enter-code-dialog"; import { showEnterCodeDialogDialog } from "../dialogs/enter-code/show-enter-code-dialog";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
@ -30,15 +31,20 @@ export const callProtectedLockService = async (
service: ProtectedLockService service: ProtectedLockService
) => { ) => {
let code: string | undefined; let code: string | undefined;
const lockRegistryEntry = await getExtendedEntityRegistryEntry(
hass,
stateObj.entity_id
).catch(() => undefined);
const defaultCode = lockRegistryEntry?.options?.lock?.default_code;
if (stateObj!.attributes.code_format) { if (stateObj!.attributes.code_format && !defaultCode) {
const response = await showEnterCodeDialogDialog(element, { const response = await showEnterCodeDialogDialog(element, {
codeFormat: "text", codeFormat: "text",
codePattern: stateObj!.attributes.code_format, codePattern: stateObj!.attributes.code_format,
title: hass.localize(`ui.card.lock.${service}`), title: hass.localize(`ui.card.lock.${service}`),
submitText: hass.localize(`ui.card.lock.${service}`), submitText: hass.localize(`ui.card.lock.${service}`),
}); });
if (!response) { if (response == null) {
throw new Error("Code dialog closed"); throw new Error("Code dialog closed");
} }
code = response; code = response;

View File

@ -207,7 +207,7 @@ export class DialogAreaFilter
color: var(--disabled-text-color); color: var(--disabled-text-color);
} }
.handle { .handle {
cursor: grab; cursor: move;
} }
.actions { .actions {
display: flex; display: flex;

View File

@ -90,7 +90,7 @@ class StepFlowForm extends LitElement {
${this._loading ${this._loading
? html` ? html`
<div class="submit-spinner"> <div class="submit-spinner">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
</div> </div>
` `
: html` : html`

View File

@ -27,7 +27,7 @@ class StepFlowLoading extends LitElement {
return html` return html`
<div class="init-spinner"> <div class="init-spinner">
${description ? html`<div>${description}</div>` : ""} ${description ? html`<div>${description}</div>` : ""}
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
</div> </div>
`; `;
} }

View File

@ -24,7 +24,7 @@ class StepFlowProgress extends LitElement {
${this.flowConfig.renderShowFormProgressHeader(this.hass, this.step)} ${this.flowConfig.renderShowFormProgressHeader(this.hass, this.step)}
</h2> </h2>
<div class="content"> <div class="content">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
${this.flowConfig.renderShowFormProgressDescription( ${this.flowConfig.renderShowFormProgressDescription(
this.hass, this.hass,
this.step this.step

View File

@ -99,6 +99,8 @@ export class DialogEnterCode
id="code" id="code"
.label=${this.hass.localize("ui.dialogs.enter_code.input_label")} .label=${this.hass.localize("ui.dialogs.enter_code.input_label")}
type="password" type="password"
autoValidate
validateOnInitialRender
pattern=${ifDefined(this._dialogParams.codePattern)} pattern=${ifDefined(this._dialogParams.codePattern)}
inputmode="text" inputmode="text"
></ha-textfield> ></ha-textfield>

View File

@ -53,8 +53,8 @@ export class MoreInfoConfigurator extends LitElement {
> >
${this._isConfiguring ${this._isConfiguring
? html`<ha-circular-progress ? html`<ha-circular-progress
active indeterminate
alt="Configuring" aria-label="Configuring"
></ha-circular-progress>` ></ha-circular-progress>`
: ""} : ""}
${this.stateObj.attributes.submit_caption} ${this.stateObj.attributes.submit_caption}

View File

@ -104,7 +104,7 @@ class MoreInfoUpdate extends LitElement {
${supportsFeature(this.stateObj!, UPDATE_SUPPORT_RELEASE_NOTES) && ${supportsFeature(this.stateObj!, UPDATE_SUPPORT_RELEASE_NOTES) &&
!this._error !this._error
? this._releaseNotes === undefined ? this._releaseNotes === undefined
? html`<ha-circular-progress active></ha-circular-progress>` ? html`<ha-circular-progress indeterminate></ha-circular-progress>`
: html`<hr /> : html`<hr />
<ha-faded> <ha-faded>
<ha-markdown .content=${this._releaseNotes}></ha-markdown> <ha-markdown .content=${this._releaseNotes}></ha-markdown>

View File

@ -214,7 +214,7 @@ export class QuickBar extends LitElement {
${!items ${!items
? html`<ha-circular-progress ? html`<ha-circular-progress
size="small" size="small"
active indeterminate
></ha-circular-progress>` ></ha-circular-progress>`
: items.length === 0 : items.length === 0
? html` ? html`
@ -375,7 +375,7 @@ export class QuickBar extends LitElement {
const spinner = document.createElement("ha-circular-progress"); const spinner = document.createElement("ha-circular-progress");
spinner.size = "small"; spinner.size = "small";
spinner.slot = "meta"; spinner.slot = "meta";
spinner.active = true; spinner.indeterminate = true;
this._getItemAtIndex(index)?.appendChild(spinner); this._getItemAtIndex(index)?.appendChild(spinner);
} }

View File

@ -91,7 +91,7 @@ class DialogRestart extends LitElement {
${this._loadingHostInfo ${this._loadingHostInfo
? html` ? html`
<div class="loader"> <div class="loader">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
</div> </div>
` `
: html` : html`

View File

@ -85,8 +85,7 @@ export class TTSTryDialog extends LitElement {
? html` ? html`
<ha-circular-progress <ha-circular-progress
size="small" size="small"
active indeterminate
alt=""
slot="primaryAction" slot="primaryAction"
class="loading" class="loading"
></ha-circular-progress> ></ha-circular-progress>

View File

@ -35,7 +35,7 @@ class HaInitPage extends LitElement {
` `
: html` : html`
<div id="progress-indicator-wrapper"> <div id="progress-indicator-wrapper">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
</div> </div>
<div id="loading-text"> <div id="loading-text">
${this.migration ${this.migration

View File

@ -46,7 +46,7 @@ class HassLoadingScreen extends LitElement {
`} `}
</div>`} </div>`}
<div class="content"> <div class="content">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
${this.message ${this.message
? html`<div id="loading-text">${this.message}</div>` ? html`<div id="loading-text">${this.message}</div>`
: nothing} : nothing}

View File

@ -57,7 +57,7 @@ class OnboardingCoreConfig extends LitElement {
} }
if (this._skipCore) { if (this._skipCore) {
return html`<div class="row center"> return html`<div class="row center">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
</div>`; </div>`;
} }
return html` return html`

View File

@ -123,7 +123,7 @@ class OnboardingLocation extends LitElement {
? html` ? html`
<ha-circular-progress <ha-circular-progress
slot="trailingIcon" slot="trailingIcon"
active indeterminate
size="small" size="small"
></ha-circular-progress> ></ha-circular-progress>
` `

View File

@ -64,8 +64,7 @@ class PanelCalendar extends LitElement {
private _end?: Date; private _end?: Date;
private _showPaneController = new ResizeController(this, { private _showPaneController = new ResizeController(this, {
callback: (entries: ResizeObserverEntry[]) => callback: (entries) => entries[0]?.contentRect.width > 750,
entries[0]?.contentRect.width > 750,
}); });
private _mql?: MediaQueryList; private _mql?: MediaQueryList;

View File

@ -227,7 +227,7 @@ export class DialogAddApplicationCredential extends LitElement {
${this._loading ${this._loading
? html` ? html`
<div slot="primaryAction" class="submit-spinner"> <div slot="primaryAction" class="submit-spinner">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
</div> </div>
` `
: html` : html`

View File

@ -101,7 +101,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
: this.hass.localize( : this.hass.localize(
"ui.panel.config.automation.editor.blueprint.no_blueprints" "ui.panel.config.automation.editor.blueprint.no_blueprints"
) )
: html`<ha-circular-progress active></ha-circular-progress>`} : html`<ha-circular-progress indeterminate></ha-circular-progress>`}
</div> </div>
${this.config.use_blueprint.path ${this.config.use_blueprint.path

View File

@ -25,19 +25,16 @@ import {
html, html,
nothing, nothing,
} from "lit"; } from "lit";
import { property, query, state } from "lit/decorators"; import { property, 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 { navigate } from "../../../common/navigate"; import { navigate } from "../../../common/navigate";
import { copyToClipboard } from "../../../common/util/copy-clipboard";
import { afterNextRender } from "../../../common/util/render-status"; import { afterNextRender } from "../../../common/util/render-status";
import "../../../components/ha-button-menu"; import "../../../components/ha-button-menu";
import "../../../components/ha-card";
import "../../../components/ha-fab"; import "../../../components/ha-fab";
import "../../../components/ha-icon-button"; import "../../../components/ha-icon-button";
import "../../../components/ha-svg-icon"; import "../../../components/ha-svg-icon";
import "../../../components/ha-yaml-editor"; import "../../../components/ha-yaml-editor";
import type { HaYamlEditor } from "../../../components/ha-yaml-editor";
import { import {
AutomationConfig, AutomationConfig,
AutomationEntity, AutomationEntity,
@ -112,8 +109,6 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
@state() private _validationErrors?: (string | TemplateResult)[]; @state() private _validationErrors?: (string | TemplateResult)[];
@query("ha-yaml-editor", true) private _yamlEditor?: HaYamlEditor;
private _configSubscriptions: Record< private _configSubscriptions: Record<
string, string,
(config?: AutomationConfig) => void (config?: AutomationConfig) => void
@ -342,8 +337,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
></manual-automation-editor> ></manual-automation-editor>
` `
: this._mode === "yaml" : this._mode === "yaml"
? html` ? html` ${this._readOnly
${this._readOnly
? html`<ha-alert alert-type="warning"> ? html`<ha-alert alert-type="warning">
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.automation.editor.read_only" "ui.panel.config.automation.editor.read_only"
@ -376,22 +370,13 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
` `
: ""} : ""}
<ha-yaml-editor <ha-yaml-editor
copyClipboard
.hass=${this.hass} .hass=${this.hass}
.defaultValue=${this._preprocessYaml()} .defaultValue=${this._preprocessYaml()}
.readOnly=${this._readOnly} .readOnly=${this._readOnly}
@value-changed=${this._yamlChanged} @value-changed=${this._yamlChanged}
></ha-yaml-editor> ></ha-yaml-editor>`
<ha-card outlined> : nothing}
<div class="card-actions">
<mwc-button @click=${this._copyYaml}>
${this.hass.localize(
"ui.panel.config.automation.editor.copy_to_clipboard"
)}
</mwc-button>
</div>
</ha-card>
`
: ``}
</div> </div>
` `
: ""} : ""}
@ -612,15 +597,6 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
return cleanConfig; return cleanConfig;
} }
private async _copyYaml(): Promise<void> {
if (this._yamlEditor?.yaml) {
await copyToClipboard(this._yamlEditor.yaml);
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),
});
}
}
private _yamlChanged(ev: CustomEvent) { private _yamlChanged(ev: CustomEvent) {
ev.stopPropagation(); ev.stopPropagation();
if (!ev.detail.isValid) { if (!ev.detail.isValid) {
@ -776,9 +752,6 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
return [ return [
haStyle, haStyle,
css` css`
ha-card {
overflow: hidden;
}
.content { .content {
padding-bottom: 20px; padding-bottom: 20px;
} }
@ -796,13 +769,11 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
} }
ha-yaml-editor { ha-yaml-editor {
flex-grow: 1; flex-grow: 1;
--actions-border-radius: 0;
--code-mirror-height: 100%; --code-mirror-height: 100%;
min-height: 0; min-height: 0;
} display: flex;
.yaml-mode ha-card { flex-direction: column;
overflow: initial;
--ha-card-border-radius: 0;
border-bottom: 1px solid var(--divider-color);
} }
p { p {
margin-bottom: 0; margin-bottom: 0;

View File

@ -158,7 +158,7 @@ class HaConfigBackup extends LitElement {
${this._backupData.backing_up ${this._backupData.backing_up
? html`<ha-circular-progress ? html`<ha-circular-progress
slot="icon" slot="icon"
active indeterminate
></ha-circular-progress>` ></ha-circular-progress>`
: html`<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>`} : html`<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>`}
</ha-fab> </ha-fab>

View File

@ -163,9 +163,9 @@ class DialogImportBlueprint extends LitElement {
> >
${this._importing ${this._importing
? html`<ha-circular-progress ? html`<ha-circular-progress
active indeterminate
size="small" size="small"
.title=${this.hass.localize( .ariaLabel=${this.hass.localize(
"ui.panel.config.blueprint.add.importing" "ui.panel.config.blueprint.add.importing"
)} )}
></ha-circular-progress>` ></ha-circular-progress>`
@ -183,9 +183,9 @@ class DialogImportBlueprint extends LitElement {
> >
${this._saving ${this._saving
? html`<ha-circular-progress ? html`<ha-circular-progress
active indeterminate
size="small" size="small"
.title=${this.hass.localize( .ariaLabel=${this.hass.localize(
"ui.panel.config.blueprint.add.saving" "ui.panel.config.blueprint.add.saving"
)} )}
></ha-circular-progress>` ></ha-circular-progress>`

View File

@ -93,7 +93,7 @@ export class CloudWebhooks extends LitElement {
? html` ? html`
<div class="progress"> <div class="progress">
<ha-circular-progress <ha-circular-progress
active indeterminate
></ha-circular-progress> ></ha-circular-progress>
</div> </div>
` `

View File

@ -108,7 +108,7 @@ class HaConfigUpdates extends SubscribeMixin(LitElement) {
></state-badge> ></state-badge>
${this.narrow && entity.attributes.in_progress ${this.narrow && entity.attributes.in_progress
? html`<ha-circular-progress ? html`<ha-circular-progress
active indeterminate
size="small" size="small"
slot="graphic" slot="graphic"
class="absolute" class="absolute"
@ -128,7 +128,7 @@ class HaConfigUpdates extends SubscribeMixin(LitElement) {
${!this.narrow ${!this.narrow
? entity.attributes.in_progress ? entity.attributes.in_progress
? html`<ha-circular-progress ? html`<ha-circular-progress
active indeterminate
size="small" size="small"
slot="meta" slot="meta"
></ha-circular-progress>` ></ha-circular-progress>`

View File

@ -164,7 +164,9 @@ export class DialogHelperDetail extends LitElement {
</mwc-button> </mwc-button>
`; `;
} else if (this._loading || this._helperFlows === undefined) { } else if (this._loading || this._helperFlows === undefined) {
content = html`<ha-circular-progress active></ha-circular-progress>`; content = html`<ha-circular-progress
indeterminate
></ha-circular-progress>`;
} else { } else {
const items: [string, string][] = []; const items: [string, string][] = [];

View File

@ -449,7 +449,7 @@ class AddIntegrationDialog extends LitElement {
> >
</lit-virtualizer> </lit-virtualizer>
</mwc-list>` </mwc-list>`
: html`<ha-circular-progress active></ha-circular-progress>`} `; : html`<ha-circular-progress indeterminate></ha-circular-progress>`} `;
} }
private _keyFunction = (integration: IntegrationListItem) => private _keyFunction = (integration: IntegrationListItem) =>

View File

@ -57,7 +57,7 @@ class DialogMatterAddDevice extends LitElement {
) )
: html`<ha-circular-progress : html`<ha-circular-progress
size="large" size="large"
active indeterminate
></ha-circular-progress>`} ></ha-circular-progress>`}
</div> </div>
<mwc-button slot="primaryAction" @click=${this.closeDialog}> <mwc-button slot="primaryAction" @click=${this.closeDialog}>

View File

@ -102,7 +102,7 @@ class DialogZHAReconfigureDevice extends LitElement {
${this._status === "started" ${this._status === "started"
? html` ? html`
<div class="flex-container"> <div class="flex-container">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
<div class="status"> <div class="status">
<p> <p>
<b> <b>

View File

@ -98,8 +98,8 @@ class ZHAAddDevicesPage extends LitElement {
)} )}
</h1> </h1>
<ha-circular-progress <ha-circular-progress
active indeterminate
alt="Searching" aria-label="Searching"
></ha-circular-progress> ></ha-circular-progress>
` `
: html` : html`

View File

@ -98,9 +98,9 @@ export class ZHAAddGroupPage extends LitElement {
> >
${this._processingAdd ${this._processingAdd
? html`<ha-circular-progress ? html`<ha-circular-progress
active indeterminate
size="small" size="small"
.title=${this.hass!.localize( .ariaLabel=${this.hass!.localize(
"ui.panel.config.zha.groups.creating_group" "ui.panel.config.zha.groups.creating_group"
)} )}
></ha-circular-progress>` ></ha-circular-progress>`

View File

@ -120,9 +120,8 @@ class ZHADeviceNeighbors extends LitElement {
return html` return html`
${!this._devices ${!this._devices
? html`<ha-circular-progress ? html`<ha-circular-progress
alt="Loading"
size="large" size="large"
active indeterminate
></ha-circular-progress>` ></ha-circular-progress>`
: html`<ha-data-table : html`<ha-data-table
.hass=${this.hass} .hass=${this.hass}

View File

@ -169,12 +169,14 @@ export class ZHAGroupPage extends LitElement {
@click=${this._removeMembersFromGroup} @click=${this._removeMembersFromGroup}
class="button" class="button"
> >
<ha-circular-progress ${this._processingRemove
?active=${this._processingRemove} ? html`<ha-circular-progress
alt=${this.hass.localize( indeterminate
"ui.panel.config.zha.groups.removing_members" .ariaLabel=${this.hass.localize(
)} "ui.panel.config.zha.groups.removing_members"
></ha-circular-progress> )}
></ha-circular-progress>`
: nothing}
${this.hass!.localize( ${this.hass!.localize(
"ui.panel.config.zha.groups.remove_members" "ui.panel.config.zha.groups.remove_members"
)}</mwc-button )}</mwc-button
@ -208,7 +210,7 @@ export class ZHAGroupPage extends LitElement {
? html`<ha-circular-progress ? html`<ha-circular-progress
active active
size="small" size="small"
title="Saving" aria-label="Saving"
></ha-circular-progress>` ></ha-circular-progress>`
: ""} : ""}
${this.hass!.localize( ${this.hass!.localize(

View File

@ -116,7 +116,10 @@ class DialogZWaveJSAddNode extends LitElement {
> >
${this._status === "loading" ${this._status === "loading"
? html`<div style="display: flex; justify-content: center;"> ? html`<div style="display: flex; justify-content: center;">
<ha-circular-progress size="large" active></ha-circular-progress> <ha-circular-progress
size="large"
indeterminate
></ha-circular-progress>
</div>` </div>`
: this._status === "choose_strategy" : this._status === "choose_strategy"
? html`<h3>Choose strategy</h3> ? html`<h3>Choose strategy</h3>
@ -288,7 +291,9 @@ class DialogZWaveJSAddNode extends LitElement {
"ui.panel.config.zwave_js.add_node.searching_device" "ui.panel.config.zwave_js.add_node.searching_device"
)} )}
</h3> </h3>
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress
indeterminate
></ha-circular-progress>
<p> <p>
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.zwave_js.add_node.follow_device_instructions" "ui.panel.config.zwave_js.add_node.follow_device_instructions"
@ -304,7 +309,7 @@ class DialogZWaveJSAddNode extends LitElement {
)} )}
</h2> </h2>
<ha-circular-progress <ha-circular-progress
active indeterminate
></ha-circular-progress> ></ha-circular-progress>
<p> <p>
${this.hass.localize( ${this.hass.localize(
@ -358,7 +363,7 @@ class DialogZWaveJSAddNode extends LitElement {
? html` ? html`
<div class="flex-container"> <div class="flex-container">
<ha-circular-progress <ha-circular-progress
active indeterminate
></ha-circular-progress> ></ha-circular-progress>
<div class="status"> <div class="status">
<p> <p>

View File

@ -97,7 +97,7 @@ class DialogZWaveJSRebuildNodeRoutes extends LitElement {
${this._status === "started" ${this._status === "started"
? html` ? html`
<div class="flex-container"> <div class="flex-container">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
<div class="status"> <div class="status">
<p> <p>
${this.hass.localize( ${this.hass.localize(

View File

@ -68,7 +68,7 @@ class DialogZWaveJSReinterviewNode extends LitElement {
${this._status === "started" ${this._status === "started"
? html` ? html`
<div class="flex-container"> <div class="flex-container">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
<div class="status"> <div class="status">
<p> <p>
<b> <b>

View File

@ -91,7 +91,7 @@ class DialogZWaveJSRemoveFailedNode extends LitElement {
${this._status === "started" ${this._status === "started"
? html` ? html`
<div class="flex-container"> <div class="flex-container">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
<div class="status"> <div class="status">
<p> <p>
<b> <b>

View File

@ -71,7 +71,7 @@ class DialogZWaveJSRemoveNode extends LitElement {
${this._status === "started" ${this._status === "started"
? html` ? html`
<div class="flex-container"> <div class="flex-container">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
<div class="status"> <div class="status">
<p> <p>
<b <b

View File

@ -171,7 +171,7 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
<div class="icon"> <div class="icon">
${this._status === "disconnected" ${this._status === "disconnected"
? html`<ha-circular-progress ? html`<ha-circular-progress
active indeterminate
></ha-circular-progress>` ></ha-circular-progress>`
: html` : html`
<ha-svg-icon <ha-svg-icon
@ -457,7 +457,7 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
: html` : html`
<ha-circular-progress <ha-circular-progress
size="small" size="small"
active indeterminate
></ha-circular-progress> ></ha-circular-progress>
`} `}
</div> </div>

View File

@ -97,7 +97,7 @@ export class SystemLogCard extends LitElement {
${this._items === undefined ${this._items === undefined
? html` ? html`
<div class="loading-container"> <div class="loading-container">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
</div> </div>
` `
: html` : html`

View File

@ -69,7 +69,7 @@ export class HassioHostname extends LitElement {
<div class="card-actions"> <div class="card-actions">
<mwc-button @click=${this._save} .disabled=${this._processing}> <mwc-button @click=${this._save} .disabled=${this._processing}>
${this._processing ${this._processing
? html`<ha-circular-progress active size="small"> ? html`<ha-circular-progress indeterminate size="small">
</ha-circular-progress>` </ha-circular-progress>`
: this.hass.localize("ui.common.save")} : this.hass.localize("ui.common.save")}
</mwc-button> </mwc-button>

View File

@ -126,7 +126,7 @@ export class HassioNetwork extends LitElement {
.disabled=${this._scanning} .disabled=${this._scanning}
> >
${this._scanning ${this._scanning
? html`<ha-circular-progress active size="small"> ? html`<ha-circular-progress indeterminate size="small">
</ha-circular-progress>` </ha-circular-progress>`
: this.hass.localize( : this.hass.localize(
"ui.panel.config.network.supervisor.scan_ap" "ui.panel.config.network.supervisor.scan_ap"
@ -242,7 +242,7 @@ export class HassioNetwork extends LitElement {
<div class="card-actions"> <div class="card-actions">
<mwc-button @click=${this._updateNetwork} .disabled=${!this._dirty}> <mwc-button @click=${this._updateNetwork} .disabled=${!this._dirty}>
${this._processing ${this._processing
? html`<ha-circular-progress active size="small"> ? html`<ha-circular-progress indeterminate size="small">
</ha-circular-progress>` </ha-circular-progress>`
: this.hass.localize("ui.common.save")} : this.hass.localize("ui.common.save")}
</mwc-button> </mwc-button>

View File

@ -304,7 +304,7 @@ class DialogSystemInformation extends LitElement {
if (!this._systemInfo) { if (!this._systemInfo) {
sections.push(html` sections.push(html`
<div class="loading-container"> <div class="loading-container">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
</div> </div>
`); `);
} else { } else {
@ -324,7 +324,10 @@ class DialogSystemInformation extends LitElement {
if (info.type === "pending") { if (info.type === "pending") {
value = html` value = html`
<ha-circular-progress active size="tiny"></ha-circular-progress> <ha-circular-progress
indeterminate
size="tiny"
></ha-circular-progress>
`; `;
} else if (info.type === "failed") { } else if (info.type === "failed") {
value = html` value = html`

View File

@ -80,7 +80,7 @@ export class HaBlueprintScriptEditor extends LitElement {
: this.hass.localize( : this.hass.localize(
"ui.panel.config.automation.editor.blueprint.no_blueprints" "ui.panel.config.automation.editor.blueprint.no_blueprints"
) )
: html`<ha-circular-progress active></ha-circular-progress>`} : html`<ha-circular-progress indeterminate></ha-circular-progress>`}
</div> </div>
${this.config.use_blueprint.path ${this.config.use_blueprint.path

View File

@ -26,7 +26,6 @@ import { fireEvent } from "../../../common/dom/fire_event";
import { navigate } from "../../../common/navigate"; import { navigate } from "../../../common/navigate";
import { slugify } from "../../../common/string/slugify"; import { slugify } from "../../../common/string/slugify";
import { computeRTL } from "../../../common/util/compute_rtl"; import { computeRTL } from "../../../common/util/compute_rtl";
import { copyToClipboard } from "../../../common/util/copy-clipboard";
import { afterNextRender } from "../../../common/util/render-status"; import { afterNextRender } from "../../../common/util/render-status";
import "../../../components/ha-button-menu"; import "../../../components/ha-button-menu";
import "../../../components/ha-card"; import "../../../components/ha-card";
@ -38,7 +37,6 @@ import type {
import "../../../components/ha-icon-button"; import "../../../components/ha-icon-button";
import "../../../components/ha-svg-icon"; import "../../../components/ha-svg-icon";
import "../../../components/ha-yaml-editor"; import "../../../components/ha-yaml-editor";
import type { HaYamlEditor } from "../../../components/ha-yaml-editor";
import { validateConfig } from "../../../data/config"; import { validateConfig } from "../../../data/config";
import { UNAVAILABLE } from "../../../data/entity"; import { UNAVAILABLE } from "../../../data/entity";
import { EntityRegistryEntry } from "../../../data/entity_registry"; import { EntityRegistryEntry } from "../../../data/entity_registry";
@ -94,8 +92,6 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
@state() private _readOnly = false; @state() private _readOnly = false;
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;
@query("manual-script-editor") @query("manual-script-editor")
private _manualEditor?: HaManualScriptEditor; private _manualEditor?: HaManualScriptEditor;
@ -405,24 +401,14 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
</div> </div>
` `
: this._mode === "yaml" : this._mode === "yaml"
? html` ? html` <ha-yaml-editor
<ha-yaml-editor copyClipboard
.hass=${this.hass} .hass=${this.hass}
.defaultValue=${this._preprocessYaml()} .defaultValue=${this._preprocessYaml()}
.readOnly=${this._readOnly} .readOnly=${this._readOnly}
@value-changed=${this._yamlChanged} @value-changed=${this._yamlChanged}
></ha-yaml-editor> ></ha-yaml-editor>`
<ha-card outlined> : nothing}
<div class="card-actions">
<mwc-button @click=${this._copyYaml}>
${this.hass.localize(
"ui.panel.config.automation.editor.copy_to_clipboard"
)}
</mwc-button>
</div>
</ha-card>
`
: ``}
</div> </div>
<ha-fab <ha-fab
slot="fab" slot="fab"
@ -735,15 +721,6 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
return this._config; return this._config;
} }
private async _copyYaml(): Promise<void> {
if (this._yamlEditor?.yaml) {
await copyToClipboard(this._yamlEditor.yaml);
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),
});
}
}
private _yamlChanged(ev: CustomEvent) { private _yamlChanged(ev: CustomEvent) {
ev.stopPropagation(); ev.stopPropagation();
if (!ev.detail.isValid) { if (!ev.detail.isValid) {
@ -903,8 +880,11 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
} }
ha-yaml-editor { ha-yaml-editor {
flex-grow: 1; flex-grow: 1;
--actions-border-radius: 0;
--code-mirror-height: 100%; --code-mirror-height: 100%;
min-height: 0; min-height: 0;
display: flex;
flex-direction: column;
} }
.yaml-mode ha-card { .yaml-mode ha-card {
overflow: initial; overflow: initial;

View File

@ -106,7 +106,11 @@ class MoveDatadiskDialog extends LitElement {
> >
${this._moving ${this._moving
? html` ? html`
<ha-circular-progress alt="Moving" size="large" active> <ha-circular-progress
aria-label="Moving"
size="large"
indeterminate
>
</ha-circular-progress> </ha-circular-progress>
<p class="progress-text"> <p class="progress-text">
${this.hass.localize( ${this.hass.localize(

View File

@ -194,7 +194,7 @@ export class DialogAddUser extends LitElement {
${this._loading ${this._loading
? html` ? html`
<div slot="primaryAction" class="submit-spinner"> <div slot="primaryAction" class="submit-spinner">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
</div> </div>
` `
: html` : html`

View File

@ -90,7 +90,7 @@ const renderProgress = (
return html``; return html``;
} }
return html` return html`
<ha-circular-progress size="tiny" active></ha-circular-progress> <ha-circular-progress size="tiny" indeterminate></ha-circular-progress>
`; `;
} }

View File

@ -184,6 +184,8 @@ class HaPanelDevService extends LitElement {
> >
<div class="card-content"> <div class="card-content">
<ha-yaml-editor <ha-yaml-editor
.hass=${this.hass}
copyClipboard
readOnly readOnly
autoUpdate autoUpdate
.value=${this._response} .value=${this._response}

View File

@ -131,7 +131,7 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement {
let stats: TemplateResult; let stats: TemplateResult;
if (!this._stats5min || !this._statsHour) { if (!this._stats5min || !this._statsHour) {
stats = html`<ha-circular-progress active></ha-circular-progress>`; stats = html`<ha-circular-progress indeterminate></ha-circular-progress>`;
} else if (this._statsHour.length < 1 && this._stats5min.length < 1) { } else if (this._statsHour.length < 1 && this._stats5min.length < 1) {
stats = html`<p> stats = html`<p>
${this.hass.localize( ${this.hass.localize(

View File

@ -156,7 +156,7 @@ class HaPanelDevTemplate extends LitElement {
${this._rendering ${this._rendering
? html`<ha-circular-progress ? html`<ha-circular-progress
class="render-spinner" class="render-spinner"
active indeterminate
size="small" size="small"
></ha-circular-progress>` ></ha-circular-progress>`
: ""} : ""}

View File

@ -77,7 +77,7 @@ export class DeveloperYamlConfig extends LitElement {
? html`<div ? html`<div
class="validate-container layout vertical center-center" class="validate-container layout vertical center-center"
> >
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
</div> ` </div> `
: nothing : nothing
: html` : html`
@ -94,7 +94,7 @@ export class DeveloperYamlConfig extends LitElement {
) )
} }
</div> </div>
${ ${
this._validateResult.errors this._validateResult.errors
? html`<ha-alert ? html`<ha-alert
@ -233,7 +233,7 @@ export class DeveloperYamlConfig extends LitElement {
} }
.validate-log { .validate-log {
white-space: pre; white-space: pre-wrap;
direction: ltr; direction: ltr;
} }

View File

@ -193,10 +193,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
</div> </div>
${this._isLoading ${this._isLoading
? html`<div class="progress-wrapper"> ? html`<div class="progress-wrapper">
<ha-circular-progress <ha-circular-progress indeterminate></ha-circular-progress>
active
alt=${this.hass.localize("ui.common.loading")}
></ha-circular-progress>
</div>` </div>`
: !this._targetPickerValue : !this._targetPickerValue
? html`<div class="start-search"> ? html`<div class="start-search">

View File

@ -107,10 +107,7 @@ export class HaLogbook extends LitElement {
if (this._logbookEntries === undefined) { if (this._logbookEntries === undefined) {
return html` return html`
<div class="progress-wrapper"> <div class="progress-wrapper">
<ha-circular-progress <ha-circular-progress indeterminate></ha-circular-progress>
active
alt=${this.hass.localize("ui.common.loading")}
></ha-circular-progress>
</div> </div>
`; `;
} }

View File

@ -1,25 +1,34 @@
import { mdiPower, mdiWaterPercent } from "@mdi/js"; import { mdiTuneVariant } from "@mdi/js";
import { HassEntity } from "home-assistant-js-websocket"; import { HassEntity } from "home-assistant-js-websocket";
import { LitElement, PropertyValues, TemplateResult, css, html } from "lit"; import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, query, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map"; import { stopPropagation } from "../../../common/dom/stop_propagation";
import { computeDomain } from "../../../common/entity/compute_domain"; import { computeDomain } from "../../../common/entity/compute_domain";
import { stateColorCss } from "../../../common/entity/state_color"; import { supportsFeature } from "../../../common/entity/supports-feature";
import "../../../components/ha-control-select"; import "../../../components/ha-control-select";
import type { ControlSelectOption } from "../../../components/ha-control-select"; import type { ControlSelectOption } from "../../../components/ha-control-select";
import "../../../components/ha-control-select-menu";
import type { HaControlSelectMenu } from "../../../components/ha-control-select-menu";
import {
HumidifierEntityFeature,
HumidifierEntity,
computeHumidiferModeIcon,
} from "../../../data/humidifier";
import { UNAVAILABLE } from "../../../data/entity"; import { UNAVAILABLE } from "../../../data/entity";
import { HumidifierEntity, HumidifierState } from "../../../data/humidifier";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature } from "../types"; import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { HumidifierModesCardFeatureConfig } from "./types"; import { HumidifierModesCardFeatureConfig } from "./types";
export const supportsHumidifierModesCardFeature = (stateObj: HassEntity) => { export const supportsHumidifierModesCardFeature = (stateObj: HassEntity) => {
const domain = computeDomain(stateObj.entity_id); const domain = computeDomain(stateObj.entity_id);
return domain === "humidifier"; return (
domain === "humidifier" &&
supportsFeature(stateObj, HumidifierEntityFeature.MODES)
);
}; };
@customElement("hui-humidifier-modes-card-feature") @customElement("hui-humidifier-modes-card-feature")
class HuiHumidifierModeCardFeature class HuiHumidifierModesCardFeature
extends LitElement extends LitElement
implements LovelaceCardFeature implements LovelaceCardFeature
{ {
@ -29,14 +38,29 @@ class HuiHumidifierModeCardFeature
@state() private _config?: HumidifierModesCardFeatureConfig; @state() private _config?: HumidifierModesCardFeatureConfig;
@state() _currentState?: HumidifierState; @state() _currentMode?: string;
static getStubConfig(): HumidifierModesCardFeatureConfig { @query("ha-control-select-menu", true)
private _haSelect?: HaControlSelectMenu;
static getStubConfig(
_,
stateObj?: HassEntity
): HumidifierModesCardFeatureConfig {
return { return {
type: "humidifier-modes", type: "humidifier-modes",
style: "dropdown",
modes: stateObj?.attributes.available_modes || [],
}; };
} }
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
await import(
"../editor/config-elements/hui-humidifier-modes-card-feature-editor"
);
return document.createElement("hui-humidifier-modes-card-feature-editor");
}
public setConfig(config: HumidifierModesCardFeatureConfig): void { public setConfig(config: HumidifierModesCardFeatureConfig): void {
if (!config) { if (!config) {
throw new Error("Invalid configuration"); throw new Error("Invalid configuration");
@ -47,33 +71,46 @@ class HuiHumidifierModeCardFeature
protected willUpdate(changedProp: PropertyValues): void { protected willUpdate(changedProp: PropertyValues): void {
super.willUpdate(changedProp); super.willUpdate(changedProp);
if (changedProp.has("stateObj") && this.stateObj) { if (changedProp.has("stateObj") && this.stateObj) {
this._currentState = this.stateObj.state as HumidifierState; this._currentMode = this.stateObj.attributes.mode;
}
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (this._haSelect && changedProps.has("hass")) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (
this.hass &&
this.hass.formatEntityAttributeValue !==
oldHass?.formatEntityAttributeValue
) {
this._haSelect.layoutOptions();
}
} }
} }
private async _valueChanged(ev: CustomEvent) { private async _valueChanged(ev: CustomEvent) {
const newState = (ev.detail as any).value as HumidifierState; const mode =
(ev.detail as any).value ?? ((ev.target as any).value as string);
if (newState === this.stateObj!.state) return; const oldMode = this.stateObj!.attributes.mode;
const oldState = this.stateObj!.state as HumidifierState; if (mode === oldMode) return;
this._currentState = newState;
this._currentMode = mode;
try { try {
await this._setState(newState); await this._setMode(mode);
} catch (err) { } catch (err) {
this._currentState = oldState; this._currentMode = oldMode;
} }
} }
private async _setState(newState: HumidifierState) { private async _setMode(mode: string) {
await this.hass!.callService( await this.hass!.callService("humidifier", "set_mode", {
"humidifier", entity_id: this.stateObj!.entity_id,
newState === "on" ? "turn_on" : "turn_off", mode: mode,
{ });
entity_id: this.stateObj!.entity_id,
}
);
} }
protected render(): TemplateResult | null { protected render(): TemplateResult | null {
@ -86,34 +123,75 @@ class HuiHumidifierModeCardFeature
return null; return null;
} }
const color = stateColorCss(this.stateObj); const stateObj = this.stateObj;
const options = ["on", "off"].map<ControlSelectOption>((entityState) => ({ const modes = stateObj.attributes.available_modes || [];
value: entityState,
label: this.hass!.formatEntityState(this.stateObj!, entityState), const options = modes
path: entityState === "on" ? mdiWaterPercent : mdiPower, .filter((mode) => (this._config!.modes || []).includes(mode))
})); .map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityAttributeValue(
this.stateObj!,
"mode",
mode
),
path: computeHumidiferModeIcon(mode),
}));
if (this._config.style === "icons") {
return html`
<div class="container">
<ha-control-select
.options=${options}
.value=${this._currentMode}
@value-changed=${this._valueChanged}
hide-label
.ariaLabel=${this.hass!.formatEntityAttributeName(stateObj, "mode")}
.disabled=${this.stateObj!.state === UNAVAILABLE}
>
</ha-control-select>
</div>
`;
}
return html` return html`
<div class="container"> <div class="container">
<ha-control-select <ha-control-select-menu
.options=${options} show-arrow
.value=${this._currentState}
@value-changed=${this._valueChanged}
hide-label hide-label
.ariaLabel=${this.hass.localize("ui.card.humidifier.state")} .label=${this.hass!.formatEntityAttributeName(stateObj, "mode")}
style=${styleMap({ .value=${this._currentMode}
"--control-select-color": color, .disabled=${this.stateObj.state === UNAVAILABLE}
})} fixedMenuPosition
.disabled=${this.stateObj!.state === UNAVAILABLE} naturalMenuWidth
@selected=${this._valueChanged}
@closed=${stopPropagation}
> >
</ha-control-select> <ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
${options.map(
(option) => html`
<ha-list-item .value=${option.value} graphic="icon">
<ha-svg-icon slot="graphic" .path=${option.path}></ha-svg-icon>
${option.label}
</ha-list-item>
`
)}
</ha-control-select-menu>
</div> </div>
`; `;
} }
static get styles() { static get styles() {
return css` return css`
ha-control-select-menu {
box-sizing: border-box;
--control-select-menu-height: 40px;
--control-select-menu-border-radius: 10px;
line-height: 1.2;
display: block;
width: 100%;
}
ha-control-select { ha-control-select {
--control-select-color: var(--feature-color); --control-select-color: var(--feature-color);
--control-select-padding: 0; --control-select-padding: 0;
@ -131,6 +209,6 @@ class HuiHumidifierModeCardFeature
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"hui-humidifier-modes-card-feature": HuiHumidifierModeCardFeature; "hui-humidifier-modes-card-feature": HuiHumidifierModesCardFeature;
} }
} }

View File

@ -0,0 +1,136 @@
import { mdiPower, mdiWaterPercent } from "@mdi/js";
import { HassEntity } from "home-assistant-js-websocket";
import { LitElement, PropertyValues, TemplateResult, css, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import { computeDomain } from "../../../common/entity/compute_domain";
import { stateColorCss } from "../../../common/entity/state_color";
import "../../../components/ha-control-select";
import type { ControlSelectOption } from "../../../components/ha-control-select";
import { UNAVAILABLE } from "../../../data/entity";
import { HumidifierEntity, HumidifierState } from "../../../data/humidifier";
import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature } from "../types";
import { HumidifierToggleCardFeatureConfig } from "./types";
export const supportsHumidifierToggleCardFeature = (stateObj: HassEntity) => {
const domain = computeDomain(stateObj.entity_id);
return domain === "humidifier";
};
@customElement("hui-humidifier-toggle-card-feature")
class HuiHumidifierToggleCardFeature
extends LitElement
implements LovelaceCardFeature
{
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public stateObj?: HumidifierEntity;
@state() private _config?: HumidifierToggleCardFeatureConfig;
@state() _currentState?: HumidifierState;
static getStubConfig(): HumidifierToggleCardFeatureConfig {
return {
type: "humidifier-toggle",
};
}
public setConfig(config: HumidifierToggleCardFeatureConfig): void {
if (!config) {
throw new Error("Invalid configuration");
}
this._config = config;
}
protected willUpdate(changedProp: PropertyValues): void {
super.willUpdate(changedProp);
if (changedProp.has("stateObj") && this.stateObj) {
this._currentState = this.stateObj.state as HumidifierState;
}
}
private async _valueChanged(ev: CustomEvent) {
const newState = (ev.detail as any).value as HumidifierState;
if (newState === this.stateObj!.state) return;
const oldState = this.stateObj!.state as HumidifierState;
this._currentState = newState;
try {
await this._setState(newState);
} catch (err) {
this._currentState = oldState;
}
}
private async _setState(newState: HumidifierState) {
await this.hass!.callService(
"humidifier",
newState === "on" ? "turn_on" : "turn_off",
{
entity_id: this.stateObj!.entity_id,
}
);
}
protected render(): TemplateResult | null {
if (
!this._config ||
!this.hass ||
!this.stateObj ||
!supportsHumidifierToggleCardFeature(this.stateObj)
) {
return null;
}
const color = stateColorCss(this.stateObj);
const options = ["on", "off"].map<ControlSelectOption>((entityState) => ({
value: entityState,
label: this.hass!.formatEntityState(this.stateObj!, entityState),
path: entityState === "on" ? mdiWaterPercent : mdiPower,
}));
return html`
<div class="container">
<ha-control-select
.options=${options}
.value=${this._currentState}
@value-changed=${this._valueChanged}
hide-label
.ariaLabel=${this.hass.localize("ui.card.humidifier.state")}
style=${styleMap({
"--control-select-color": color,
})}
.disabled=${this.stateObj!.state === UNAVAILABLE}
>
</ha-control-select>
</div>
`;
}
static get styles() {
return css`
ha-control-select {
--control-select-color: var(--feature-color);
--control-select-padding: 0;
--control-select-thickness: 40px;
--control-select-border-radius: 10px;
--control-select-button-border-radius: 10px;
}
.container {
padding: 0 12px 12px 12px;
width: auto;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-humidifier-toggle-card-feature": HuiHumidifierToggleCardFeature;
}
}

View File

@ -0,0 +1,127 @@
import { HassEntity } from "home-assistant-js-websocket";
import { css, html, LitElement, nothing, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import "../../../components/ha-control-slider";
import { UNAVAILABLE } from "../../../data/entity";
import { HumidifierEntity } from "../../../data/humidifier";
import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature } from "../types";
import { TargetHumidityCardFeatureConfig } from "./types";
export const supportsTargetHumidityCardFeature = (stateObj: HassEntity) => {
const domain = computeDomain(stateObj.entity_id);
return domain === "humidifier";
};
@customElement("hui-target-humidity-card-feature")
class HuiTargetHumidityCardFeature
extends LitElement
implements LovelaceCardFeature
{
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public stateObj?: HumidifierEntity;
@state() private _config?: TargetHumidityCardFeatureConfig;
@state() private _targetHumidity?: number;
static getStubConfig(): TargetHumidityCardFeatureConfig {
return {
type: "target-humidity",
};
}
public setConfig(config: TargetHumidityCardFeatureConfig): void {
if (!config) {
throw new Error("Invalid configuration");
}
this._config = config;
}
protected willUpdate(changedProp: PropertyValues): void {
super.willUpdate(changedProp);
if (changedProp.has("stateObj")) {
this._targetHumidity = this.stateObj!.attributes.humidity;
}
}
private get _step() {
return 1;
}
private get _min() {
return this.stateObj!.attributes.min_humidity ?? 0;
}
private get _max() {
return this.stateObj!.attributes.max_humidity ?? 100;
}
private _valueChanged(ev: CustomEvent) {
const value = (ev.detail as any).value;
if (isNaN(value)) return;
this._targetHumidity = value;
this._callService();
}
private _callService() {
this.hass!.callService("humidifier", "set_humidity", {
entity_id: this.stateObj!.entity_id,
humidity: this._targetHumidity,
});
}
protected render() {
if (
!this._config ||
!this.hass ||
!this.stateObj ||
!supportsTargetHumidityCardFeature(this.stateObj)
) {
return nothing;
}
return html`
<div class="container">
<ha-control-slider
.value=${this.stateObj.attributes.humidity}
.min=${this._min}
.max=${this._max}
.step=${this._step}
.disabled=${this.stateObj!.state === UNAVAILABLE}
@value-changed=${this._valueChanged}
.label=${this.hass.formatEntityAttributeName(
this.stateObj,
"humidity"
)}
unit="%"
.locale=${this.hass.locale}
></ha-control-slider>
</div>
`;
}
static get styles() {
return css`
ha-control-slider {
--control-slider-color: var(--feature-color);
--control-slider-background: var(--feature-color);
--control-slider-background-opacity: 0.2;
--control-slider-thickness: 40px;
--control-slider-border-radius: 10px;
}
.container {
padding: 0 12px 12px 12px;
width: auto;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-target-humidity-card-feature": HuiTargetHumidityCardFeature;
}
}

View File

@ -55,6 +55,10 @@ export interface NumericInputCardFeatureConfig {
style?: "buttons" | "slider"; style?: "buttons" | "slider";
} }
export interface TargetHumidityCardFeatureConfig {
type: "target-humidity";
}
export interface TargetTemperatureCardFeatureConfig { export interface TargetTemperatureCardFeatureConfig {
type: "target-temperature"; type: "target-temperature";
} }
@ -66,6 +70,12 @@ export interface WaterHeaterOperationModesCardFeatureConfig {
export interface HumidifierModesCardFeatureConfig { export interface HumidifierModesCardFeatureConfig {
type: "humidifier-modes"; type: "humidifier-modes";
style?: "dropdown" | "icons";
modes?: string[];
}
export interface HumidifierToggleCardFeatureConfig {
type: "humidifier-toggle";
} }
export const VACUUM_COMMANDS = [ export const VACUUM_COMMANDS = [
@ -101,11 +111,13 @@ export type LovelaceCardFeatureConfig =
| CoverTiltPositionCardFeatureConfig | CoverTiltPositionCardFeatureConfig
| CoverTiltCardFeatureConfig | CoverTiltCardFeatureConfig
| FanSpeedCardFeatureConfig | FanSpeedCardFeatureConfig
| HumidifierToggleCardFeatureConfig
| HumidifierModesCardFeatureConfig | HumidifierModesCardFeatureConfig
| LawnMowerCommandsCardFeatureConfig | LawnMowerCommandsCardFeatureConfig
| LightBrightnessCardFeatureConfig | LightBrightnessCardFeatureConfig
| LightColorTempCardFeatureConfig | LightColorTempCardFeatureConfig
| VacuumCommandsCardFeatureConfig | VacuumCommandsCardFeatureConfig
| TargetHumidityCardFeatureConfig
| TargetTemperatureCardFeatureConfig | TargetTemperatureCardFeatureConfig
| WaterHeaterOperationModesCardFeatureConfig | WaterHeaterOperationModesCardFeatureConfig
| SelectOptionsCardFeatureConfig | SelectOptionsCardFeatureConfig

View File

@ -51,7 +51,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
entity: foundEntities[0] || "", entity: foundEntities[0] || "",
features: [ features: [
{ {
type: "humidifier-modes", type: "humidifier-toggle",
}, },
], ],
}; };
@ -180,6 +180,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
max-width: 344px; /* 12px + 12px + 320px */ max-width: 344px; /* 12px + 12px + 320px */
padding: 0 12px 12px 12px; padding: 0 12px 12px 12px;
box-sizing: border-box; box-sizing: border-box;
--interaction-margin: 0px;
} }
.more-info { .more-info {

View File

@ -101,6 +101,8 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
} }
this._config = config; this._config = config;
this.updateComplete.then(() => this._measureCard());
} }
public connectedCallback(): void { public connectedCallback(): void {
@ -339,15 +341,12 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
protected firstUpdated(): void { protected firstUpdated(): void {
this._attachObserver(); this._attachObserver();
this._measureCard();
} }
public willUpdate(changedProps: PropertyValues): void { public willUpdate(changedProps: PropertyValues): void {
super.willUpdate(changedProps); super.willUpdate(changedProps);
if (!this.hasUpdated) {
this._measureCard();
}
if ( if (
!this._config || !this._config ||
!this.hass || !this.hass ||
@ -468,6 +467,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
private _measureCard() { private _measureCard() {
const card = this.shadowRoot!.querySelector("ha-card"); const card = this.shadowRoot!.querySelector("ha-card");
if (!card) { if (!card) {
return; return;
} }

View File

@ -46,7 +46,7 @@ export class HuiStartingCard extends LitElement implements LovelaceCard {
return html` return html`
<div class="content"> <div class="content">
<ha-circular-progress active></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
${this.hass.localize("ui.panel.lovelace.cards.starting.description")} ${this.hass.localize("ui.panel.lovelace.cards.starting.description")}
</div> </div>
`; `;

View File

@ -172,6 +172,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
max-width: 344px; /* 12px + 12px + 320px */ max-width: 344px; /* 12px + 12px + 320px */
padding: 0 12px 12px 12px; padding: 0 12px 12px 12px;
box-sizing: border-box; box-sizing: border-box;
--interaction-margin: 0px;
} }
.more-info { .more-info {

View File

@ -22,6 +22,7 @@ import memoizeOne from "memoize-one";
import type { SortableEvent } from "sortablejs"; import type { SortableEvent } from "sortablejs";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { supportsFeature } from "../../../common/entity/supports-feature"; import { supportsFeature } from "../../../common/entity/supports-feature";
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-checkbox"; import "../../../components/ha-checkbox";
import "../../../components/ha-icon-button"; import "../../../components/ha-icon-button";
@ -347,7 +348,7 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
.title=${this.hass!.localize( .title=${this.hass!.localize(
"ui.panel.lovelace.cards.todo-list.drag_and_drop" "ui.panel.lovelace.cards.todo-list.drag_and_drop"
)} )}
class="reorderButton" class="reorderButton handle"
.path=${mdiDrag} .path=${mdiDrag}
> >
</ha-svg-icon> </ha-svg-icon>
@ -439,7 +440,21 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
} }
const checkedItems = this._getCheckedItems(this._items); const checkedItems = this._getCheckedItems(this._items);
const uids = checkedItems.map((item: TodoItem) => item.uid); const uids = checkedItems.map((item: TodoItem) => item.uid);
deleteItems(this.hass!, this._entityId!, uids); showConfirmationDialog(this, {
title: this.hass.localize(
"ui.panel.lovelace.cards.todo-list.delete_confirm_title"
),
text: this.hass.localize(
"ui.panel.lovelace.cards.todo-list.delete_confirm_text",
{ number: uids.length }
),
dismissText: this.hass.localize("ui.common.cancel"),
confirmText: this.hass.localize("ui.common.delete"),
destructive: true,
confirm: () => {
deleteItems(this.hass!, this._entityId!, uids);
},
});
} }
private get _newItem(): HaTextField { private get _newItem(): HaTextField {
@ -583,6 +598,10 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
direction: var(--direction); direction: var(--direction);
} }
.handle {
cursor: move;
}
ha-checkbox { ha-checkbox {
margin-left: -12px; margin-left: -12px;
margin-inline-start: -12px; margin-inline-start: -12px;

View File

@ -263,7 +263,7 @@ export class HuiImage extends LitElement {
> >
<ha-circular-progress <ha-circular-progress
class="render-spinner" class="render-spinner"
active indeterminate
size="small" size="small"
></ha-circular-progress> ></ha-circular-progress>
</div>` </div>`

View File

@ -7,12 +7,14 @@ import "../card-features/hui-cover-tilt-card-feature";
import "../card-features/hui-cover-tilt-position-card-feature"; import "../card-features/hui-cover-tilt-position-card-feature";
import "../card-features/hui-fan-speed-card-feature"; import "../card-features/hui-fan-speed-card-feature";
import "../card-features/hui-humidifier-modes-card-feature"; import "../card-features/hui-humidifier-modes-card-feature";
import "../card-features/hui-humidifier-toggle-card-feature";
import "../card-features/hui-lawn-mower-commands-card-feature"; import "../card-features/hui-lawn-mower-commands-card-feature";
import "../card-features/hui-light-brightness-card-feature"; import "../card-features/hui-light-brightness-card-feature";
import "../card-features/hui-light-color-temp-card-feature"; import "../card-features/hui-light-color-temp-card-feature";
import "../card-features/hui-numeric-input-card-feature"; import "../card-features/hui-numeric-input-card-feature";
import "../card-features/hui-select-options-card-feature"; import "../card-features/hui-select-options-card-feature";
import "../card-features/hui-target-temperature-card-feature"; import "../card-features/hui-target-temperature-card-feature";
import "../card-features/hui-target-humidity-card-feature";
import "../card-features/hui-vacuum-commands-card-feature"; import "../card-features/hui-vacuum-commands-card-feature";
import "../card-features/hui-water-heater-operation-modes-card-feature"; import "../card-features/hui-water-heater-operation-modes-card-feature";
import { LovelaceCardFeatureConfig } from "../card-features/types"; import { LovelaceCardFeatureConfig } from "../card-features/types";
@ -31,11 +33,13 @@ const TYPES: Set<LovelaceCardFeatureConfig["type"]> = new Set([
"cover-tilt", "cover-tilt",
"fan-speed", "fan-speed",
"humidifier-modes", "humidifier-modes",
"humidifier-toggle",
"lawn-mower-commands", "lawn-mower-commands",
"light-brightness", "light-brightness",
"light-color-temp", "light-color-temp",
"numeric-input", "numeric-input",
"select-options", "select-options",
"target-humidity",
"target-temperature", "target-temperature",
"vacuum-commands", "vacuum-commands",
"water-heater-operation-modes", "water-heater-operation-modes",

View File

@ -142,8 +142,7 @@ export class HuiCardPicker extends LitElement {
html` html`
<div class="card spinner"> <div class="card spinner">
<ha-circular-progress <ha-circular-progress
active indeterminate
alt="Loading"
></ha-circular-progress> ></ha-circular-progress>
</div> </div>
` `
@ -238,7 +237,7 @@ export class HuiCardPicker extends LitElement {
this._renderCardElement(card), this._renderCardElement(card),
html` html`
<div class="card spinner"> <div class="card spinner">
<ha-circular-progress active alt="Loading"></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
</div> </div>
` `
)}`, )}`,

View File

@ -235,8 +235,8 @@ export class HuiDialogEditCard
${this._error ${this._error
? html` ? html`
<ha-circular-progress <ha-circular-progress
active indeterminate
alt="Can't update card" aria-label="Can't update card"
></ha-circular-progress> ></ha-circular-progress>
` `
: ``} : ``}
@ -271,8 +271,8 @@ export class HuiDialogEditCard
${this._saving ${this._saving
? html` ? html`
<ha-circular-progress <ha-circular-progress
active indeterminate
title="Saving" aria-label="Saving"
size="small" size="small"
></ha-circular-progress> ></ha-circular-progress>
` `

View File

@ -110,8 +110,8 @@ export class HuiDialogSuggestCard extends LitElement {
${this._saving ${this._saving
? html` ? html`
<ha-circular-progress <ha-circular-progress
active indeterminate
title="Saving" aria-label="Saving"
size="small" size="small"
></ha-circular-progress> ></ha-circular-progress>
` `

View File

@ -29,12 +29,14 @@ import { supportsCoverPositionCardFeature } from "../../card-features/hui-cover-
import { supportsCoverTiltCardFeature } from "../../card-features/hui-cover-tilt-card-feature"; import { supportsCoverTiltCardFeature } from "../../card-features/hui-cover-tilt-card-feature";
import { supportsCoverTiltPositionCardFeature } from "../../card-features/hui-cover-tilt-position-card-feature"; import { supportsCoverTiltPositionCardFeature } from "../../card-features/hui-cover-tilt-position-card-feature";
import { supportsFanSpeedCardFeature } from "../../card-features/hui-fan-speed-card-feature"; import { supportsFanSpeedCardFeature } from "../../card-features/hui-fan-speed-card-feature";
import { supportsHumidifierToggleCardFeature } from "../../card-features/hui-humidifier-toggle-card-feature";
import { supportsHumidifierModesCardFeature } from "../../card-features/hui-humidifier-modes-card-feature"; import { supportsHumidifierModesCardFeature } from "../../card-features/hui-humidifier-modes-card-feature";
import { supportsLawnMowerCommandCardFeature } from "../../card-features/hui-lawn-mower-commands-card-feature"; import { supportsLawnMowerCommandCardFeature } from "../../card-features/hui-lawn-mower-commands-card-feature";
import { supportsLightBrightnessCardFeature } from "../../card-features/hui-light-brightness-card-feature"; import { supportsLightBrightnessCardFeature } from "../../card-features/hui-light-brightness-card-feature";
import { supportsLightColorTempCardFeature } from "../../card-features/hui-light-color-temp-card-feature"; import { supportsLightColorTempCardFeature } from "../../card-features/hui-light-color-temp-card-feature";
import { supportsNumericInputCardFeature } from "../../card-features/hui-numeric-input-card-feature"; import { supportsNumericInputCardFeature } from "../../card-features/hui-numeric-input-card-feature";
import { supportsSelectOptionsCardFeature } from "../../card-features/hui-select-options-card-feature"; import { supportsSelectOptionsCardFeature } from "../../card-features/hui-select-options-card-feature";
import { supportsTargetHumidityCardFeature } from "../../card-features/hui-target-humidity-card-feature";
import { supportsTargetTemperatureCardFeature } from "../../card-features/hui-target-temperature-card-feature"; import { supportsTargetTemperatureCardFeature } from "../../card-features/hui-target-temperature-card-feature";
import { supportsVacuumCommandsCardFeature } from "../../card-features/hui-vacuum-commands-card-feature"; import { supportsVacuumCommandsCardFeature } from "../../card-features/hui-vacuum-commands-card-feature";
import { supportsWaterHeaterOperationModesCardFeature } from "../../card-features/hui-water-heater-operation-modes-card-feature"; import { supportsWaterHeaterOperationModesCardFeature } from "../../card-features/hui-water-heater-operation-modes-card-feature";
@ -54,10 +56,12 @@ const UI_FEATURE_TYPES = [
"cover-tilt", "cover-tilt",
"fan-speed", "fan-speed",
"humidifier-modes", "humidifier-modes",
"humidifier-toggle",
"lawn-mower-commands", "lawn-mower-commands",
"light-brightness", "light-brightness",
"light-color-temp", "light-color-temp",
"select-options", "select-options",
"target-humidity",
"target-temperature", "target-temperature",
"vacuum-commands", "vacuum-commands",
"water-heater-operation-modes", "water-heater-operation-modes",
@ -70,6 +74,7 @@ const EDITABLES_FEATURE_TYPES = new Set<UiFeatureTypes>([
"vacuum-commands", "vacuum-commands",
"alarm-modes", "alarm-modes",
"climate-hvac-modes", "climate-hvac-modes",
"humidifier-modes",
"water-heater-operation-modes", "water-heater-operation-modes",
"lawn-mower-commands", "lawn-mower-commands",
"climate-preset-modes", "climate-preset-modes",
@ -89,10 +94,12 @@ const SUPPORTS_FEATURE_TYPES: Record<
"cover-tilt": supportsCoverTiltCardFeature, "cover-tilt": supportsCoverTiltCardFeature,
"fan-speed": supportsFanSpeedCardFeature, "fan-speed": supportsFanSpeedCardFeature,
"humidifier-modes": supportsHumidifierModesCardFeature, "humidifier-modes": supportsHumidifierModesCardFeature,
"humidifier-toggle": supportsHumidifierToggleCardFeature,
"lawn-mower-commands": supportsLawnMowerCommandCardFeature, "lawn-mower-commands": supportsLawnMowerCommandCardFeature,
"light-brightness": supportsLightBrightnessCardFeature, "light-brightness": supportsLightBrightnessCardFeature,
"light-color-temp": supportsLightColorTempCardFeature, "light-color-temp": supportsLightColorTempCardFeature,
"numeric-input": supportsNumericInputCardFeature, "numeric-input": supportsNumericInputCardFeature,
"target-humidity": supportsTargetHumidityCardFeature,
"target-temperature": supportsTargetTemperatureCardFeature, "target-temperature": supportsTargetTemperatureCardFeature,
"vacuum-commands": supportsVacuumCommandsCardFeature, "vacuum-commands": supportsVacuumCommandsCardFeature,
"water-heater-operation-modes": supportsWaterHeaterOperationModesCardFeature, "water-heater-operation-modes": supportsWaterHeaterOperationModesCardFeature,

View File

@ -14,15 +14,22 @@ import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-form/ha-form"; import "../../../../components/ha-form/ha-form";
import type { SchemaUnion } from "../../../../components/ha-form/types"; import type { SchemaUnion } from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
import type { HumidifierCardConfig } from "../../cards/types";
import { import {
LovelaceCardFeatureConfig, LovelaceCardFeatureConfig,
LovelaceCardFeatureContext, LovelaceCardFeatureContext,
} from "../../card-features/types"; } from "../../card-features/types";
import type { HumidifierCardConfig } from "../../cards/types";
import type { LovelaceCardEditor } from "../../types"; import type { LovelaceCardEditor } from "../../types";
import "../hui-sub-element-editor";
import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { EditSubElementEvent, SubElementEditorConfig } from "../types"; import { EditSubElementEvent, SubElementEditorConfig } from "../types";
import "./hui-card-features-editor"; import "./hui-card-features-editor";
import type { FeatureType } from "./hui-card-features-editor";
const COMPATIBLE_FEATURES_TYPES: FeatureType[] = [
"humidifier-modes",
"humidifier-toggle",
];
const cardConfigStruct = assign( const cardConfigStruct = assign(
baseLovelaceCardConfig, baseLovelaceCardConfig,
@ -103,6 +110,7 @@ export class HuiHumidifierCardEditor
<hui-card-features-editor <hui-card-features-editor
.hass=${this.hass} .hass=${this.hass}
.stateObj=${stateObj} .stateObj=${stateObj}
.featuresTypes=${COMPATIBLE_FEATURES_TYPES}
.features=${this._config!.features ?? []} .features=${this._config!.features ?? []}
@features-changed=${this._featuresChanged} @features-changed=${this._featuresChanged}
@edit-detail-element=${this._editDetailElement} @edit-detail-element=${this._editDetailElement}

View File

@ -0,0 +1,129 @@
import { HassEntity } from "home-assistant-js-websocket";
import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event";
import { FormatEntityAttributeValueFunc } from "../../../../common/translations/entity-state";
import { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/ha-form/ha-form";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types";
import {
HumidifierModesCardFeatureConfig,
LovelaceCardFeatureContext,
} from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
@customElement("hui-humidifier-modes-card-feature-editor")
export class HuiHumidifierModesCardFeatureEditor
extends LitElement
implements LovelaceCardFeatureEditor
{
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
@state() private _config?: HumidifierModesCardFeatureConfig;
public setConfig(config: HumidifierModesCardFeatureConfig): void {
this._config = config;
}
private _schema = memoizeOne(
(
localize: LocalizeFunc,
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
stateObj?: HassEntity
) =>
[
{
name: "style",
selector: {
select: {
multiple: false,
mode: "list",
options: ["dropdown", "icons"].map((mode) => ({
value: mode,
label: localize(
`ui.panel.lovelace.editor.features.types.humidifier-modes.style_list.${mode}`
),
})),
},
},
},
{
name: "modes",
selector: {
select: {
multiple: true,
mode: "list",
options:
stateObj?.attributes.available_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(stateObj, "mode", mode),
})) || [],
},
},
},
] as const satisfies readonly HaFormSchema[]
);
protected render() {
if (!this.hass || !this._config) {
return nothing;
}
const stateObj = this.context?.entity_id
? this.hass.states[this.context?.entity_id]
: undefined;
const data: HumidifierModesCardFeatureConfig = {
style: "dropdown",
modes: [],
...this._config,
};
const schema = this._schema(
this.hass.localize,
this.hass.formatEntityAttributeValue,
stateObj
);
return html`
<ha-form
.hass=${this.hass}
.data=${data}
.schema=${schema}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
></ha-form>
`;
}
private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
}
private _computeLabelCallback = (
schema: SchemaUnion<ReturnType<typeof this._schema>>
) => {
switch (schema.name) {
case "style":
case "modes":
return this.hass!.localize(
`ui.panel.lovelace.editor.features.types.humidifier-modes.${schema.name}`
);
default:
return "";
}
};
}
declare global {
interface HTMLElementTagNameMap {
"hui-humidifier-modes-card-feature-editor": HuiHumidifierModesCardFeatureEditor;
}
}

View File

@ -87,6 +87,8 @@ const HIDDEN_ATTRIBUTES = [
"unit_of_measurement", "unit_of_measurement",
"visibility_unit", "visibility_unit",
"wind_speed_unit", "wind_speed_unit",
"battery_icon",
"battery_level",
]; ];
const cardConfigStruct = assign( const cardConfigStruct = assign(

View File

@ -132,9 +132,9 @@ export class HuiSaveConfig extends LitElement implements HassDialog {
> >
${this._saving ${this._saving
? html`<ha-circular-progress ? html`<ha-circular-progress
active indeterminate
size="small" size="small"
title="Saving" aria-label="Saving"
></ha-circular-progress>` ></ha-circular-progress>`
: ""} : ""}
${this.hass!.localize( ${this.hass!.localize(

View File

@ -207,8 +207,7 @@ export abstract class HuiElementEditor<T, C = any> extends LitElement {
${this._loading ${this._loading
? html` ? html`
<ha-circular-progress <ha-circular-progress
active indeterminate
alt="Loading"
class="center margin-bot" class="center margin-bot"
></ha-circular-progress> ></ha-circular-progress>
` `

View File

@ -63,9 +63,9 @@ export class HuiDialogEditLovelace extends LitElement {
> >
${this._saving ${this._saving
? html`<ha-circular-progress ? html`<ha-circular-progress
active indeterminate
size="small" size="small"
title="Saving" aria-label="Saving"
></ha-circular-progress>` ></ha-circular-progress>`
: ""} : ""}
${this.hass!.localize("ui.common.save")}</mwc-button ${this.hass!.localize("ui.common.save")}</mwc-button

View File

@ -323,9 +323,9 @@ export class HuiDialogEditView extends LitElement {
> >
${this._saving ${this._saving
? html`<ha-circular-progress ? html`<ha-circular-progress
active indeterminate
size="small" size="small"
title="Saving" aria-label="Saving"
></ha-circular-progress>` ></ha-circular-progress>`
: ""} : ""}
${this.hass!.localize("ui.common.save")}</mwc-button ${this.hass!.localize("ui.common.save")}</mwc-button
@ -528,7 +528,7 @@ export class HuiDialogEditView extends LitElement {
ha-circular-progress { ha-circular-progress {
display: none; display: none;
} }
ha-circular-progress[active] { ha-circular-progress[indeterminate] {
display: block; display: block;
} }
.selected_menu_item { .selected_menu_item {

View File

@ -111,7 +111,10 @@ export class HuiGraphHeaderFooter
if (!this._coordinates) { if (!this._coordinates) {
return html` return html`
<div class="container"> <div class="container">
<ha-circular-progress active size="small"></ha-circular-progress> <ha-circular-progress
indeterminate
size="small"
></ha-circular-progress>
</div> </div>
`; `;
} }

Some files were not shown because too many files have changed in this diff Show More