Compare commits

..

18 Commits

Author SHA1 Message Date
Wendelin
643942f350 Update ha-input and supervisor-network styles for improved layout 2026-02-27 11:36:54 +01:00
Wendelin
544a0c2971 Refactor ha-textfield to integrate ha-input component 2026-02-27 11:25:49 +01:00
Wendelin
1b367e85da Migrate ha-md-textfield 2026-02-27 10:57:25 +01:00
Wendelin
820c8d7975 Remove ha-password-field and migrate to ha-input 2026-02-27 10:15:51 +01:00
Wendelin
ab966d039a Add ha-input component and update ha-timer-form to use it 2026-02-26 13:25:13 +01:00
Wendelin
d06321ed43 Fix protocols dashboards fab padding (#29847) 2026-02-26 10:31:50 +02:00
dependabot[bot]
3c3d8d9974 Bump rollup from 2.79.2 to 2.80.0 (#29841)
Bumps [rollup](https://github.com/rollup/rollup) from 2.79.2 to 2.80.0.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/v2.80.0/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v2.79.2...v2.80.0)

---
updated-dependencies:
- dependency-name: rollup
  dependency-version: 2.80.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-26 08:18:15 +02:00
Paul Bottein
4f39fa482d Only ask to refresh dashboard in edit mode or yaml mode (#29826) 2026-02-26 08:16:21 +02:00
renovate[bot]
5d0fe3236c Update dependency @swc/helpers to v0.5.19 (#29836)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-26 07:07:37 +01:00
renovate[bot]
b86142ae50 Update Node.js to v24.14.0 (#29831)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-25 19:25:42 +00:00
renovate[bot]
5d2f3ee5e8 Update dependency tar to v7.5.9 (#29832)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-25 19:24:58 +00:00
AlCalzone
e3f7c631a7 Rename "Z-Wave JS" to "Z-Wave" when not referring to the project/org (#29830) 2026-02-25 19:15:16 +00:00
renovate[bot]
49f9d95853 Update dependency vite-tsconfig-paths to v6.1.1 (#29829)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-25 18:53:12 +01:00
renovate[bot]
db3d7701b5 Update dependency typescript-eslint to v8.56.0 (#29828)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-25 17:35:36 +00:00
renovate[bot]
3e55acf531 Update dependency @home-assistant/webawesome to v3.2.1-ha.3 (#29810)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-25 18:26:47 +01:00
renovate[bot]
f102618d9d Update dependency eslint-plugin-wc to v3.1.0 (#29824)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-25 18:25:06 +01:00
renovate[bot]
a3c02b511d Update dependency jsdom to v28.1.0 (#29825)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-25 18:24:38 +01:00
Bram Kragten
74111d248e Fix css minifying (#29827) 2026-02-25 17:53:50 +01:00
59 changed files with 1691 additions and 1372 deletions

2
.nvmrc
View File

@@ -1 +1 @@
24.13.1
24.14.0

View File

@@ -515,14 +515,6 @@ export class DemoHaAdaptiveDialog extends LitElement {
<td><code>--ha-dialog-surface-background</code></td>
<td>Dialog/sheet background color.</td>
</tr>
<tr>
<td><code>--ha-dialog-surface-backdrop-filter</code></td>
<td>Dialog/sheet surface backdrop filter.</td>
</tr>
<tr>
<td><code>--dialog-box-shadow</code></td>
<td>Dialog surface box shadow (dialog mode only).</td>
</tr>
<tr>
<td><code>--ha-dialog-border-radius</code></td>
<td>Border radius of the dialog surface (dialog mode only).</td>
@@ -535,34 +527,6 @@ export class DemoHaAdaptiveDialog extends LitElement {
<td><code>--ha-dialog-hide-duration</code></td>
<td>Hide animation duration (dialog mode only).</td>
</tr>
<tr>
<td><code>--ha-dialog-scrim-backdrop-filter</code></td>
<td>Dialog/sheet scrim backdrop filter.</td>
</tr>
<tr>
<td><code>--dialog-backdrop-filter</code></td>
<td>Dialog/sheet scrim backdrop filter (legacy fallback).</td>
</tr>
<tr>
<td><code>--mdc-dialog-scrim-color</code></td>
<td>Dialog/sheet scrim color (legacy compatibility).</td>
</tr>
<tr>
<td><code>--ha-bottom-sheet-surface-background</code></td>
<td>Bottom sheet background color (sheet mode only).</td>
</tr>
<tr>
<td><code>--ha-bottom-sheet-surface-backdrop-filter</code></td>
<td>Bottom sheet surface backdrop filter (sheet mode only).</td>
</tr>
<tr>
<td><code>--ha-bottom-sheet-scrim-backdrop-filter</code></td>
<td>Bottom sheet scrim backdrop filter (sheet mode only).</td>
</tr>
<tr>
<td><code>--ha-bottom-sheet-scrim-color</code></td>
<td>Bottom sheet scrim color (sheet mode only).</td>
</tr>
</tbody>
</table>

View File

@@ -380,29 +380,13 @@ export class DemoHaDialog extends LitElement {
<td><code>--ha-dialog-surface-background</code></td>
<td>Dialog background color.</td>
</tr>
<tr>
<td><code>--ha-dialog-surface-backdrop-filter</code></td>
<td>Backdrop filter applied to the dialog surface.</td>
</tr>
<tr>
<td><code>--dialog-box-shadow</code></td>
<td>Dialog surface box shadow.</td>
</tr>
<tr>
<td><code>--ha-dialog-border-radius</code></td>
<td>Border radius of the dialog surface.</td>
</tr>
<tr>
<td><code>--ha-dialog-scrim-backdrop-filter</code></td>
<td>Backdrop filter applied to the dialog scrim.</td>
</tr>
<tr>
<td><code>--dialog-backdrop-filter</code></td>
<td>Legacy fallback for the dialog scrim backdrop filter.</td>
</tr>
<tr>
<td><code>--mdc-dialog-scrim-color</code></td>
<td>Dialog scrim color (legacy compatibility).</td>
<td><code>--dialog-z-index</code></td>
<td>Z-index for the dialog.</td>
</tr>
<tr>
<td><code>--dialog-surface-margin-top</code></td>

View File

@@ -52,7 +52,7 @@
"@fullcalendar/list": "6.1.20",
"@fullcalendar/luxon3": "6.1.20",
"@fullcalendar/timegrid": "6.1.20",
"@home-assistant/webawesome": "3.2.1-ha.2",
"@home-assistant/webawesome": "3.2.1-ha.3",
"@lezer/highlight": "1.2.3",
"@lit-labs/motion": "1.1.0",
"@lit-labs/observers": "2.1.0",
@@ -83,7 +83,7 @@
"@mdi/js": "7.4.47",
"@mdi/svg": "7.4.47",
"@replit/codemirror-indentation-markers": "6.5.3",
"@swc/helpers": "0.5.18",
"@swc/helpers": "0.5.19",
"@thomasloven/round-slider": "0.6.0",
"@tsparticles/engine": "3.9.1",
"@tsparticles/preset-links": "3.2.0",
@@ -172,7 +172,7 @@
"@types/mocha": "10.0.10",
"@types/qrcode": "1.5.6",
"@types/sortablejs": "1.15.9",
"@types/tar": "6.1.13",
"@types/tar": "7.0.87",
"@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29",
"@vitest/coverage-v8": "4.0.18",
@@ -188,7 +188,7 @@
"eslint-plugin-lit": "2.2.1",
"eslint-plugin-lit-a11y": "5.1.1",
"eslint-plugin-unused-imports": "4.4.1",
"eslint-plugin-wc": "3.0.2",
"eslint-plugin-wc": "3.1.0",
"fancy-log": "2.0.0",
"fs-extra": "11.3.3",
"glob": "13.0.6",
@@ -198,7 +198,7 @@
"gulp-rename": "2.1.0",
"html-minifier-terser": "7.2.0",
"husky": "9.1.7",
"jsdom": "28.0.0",
"jsdom": "28.1.0",
"jszip": "3.10.1",
"lint-staged": "16.2.7",
"lit-analyzer": "2.0.3",
@@ -210,12 +210,12 @@
"rspack-manifest-plugin": "5.2.1",
"serve": "14.2.5",
"sinon": "21.0.1",
"tar": "7.5.8",
"tar": "7.5.9",
"terser-webpack-plugin": "5.3.16",
"ts-lit-plugin": "2.0.2",
"typescript": "5.9.3",
"typescript-eslint": "8.54.0",
"vite-tsconfig-paths": "6.0.5",
"typescript-eslint": "8.56.0",
"vite-tsconfig-paths": "6.1.1",
"vitest": "4.0.18",
"webpack-stats-plugin": "1.1.3",
"webpackbar": "7.0.0",
@@ -235,6 +235,6 @@
},
"packageManager": "yarn@4.12.0",
"volta": {
"node": "24.13.1"
"node": "24.14.0"
}
}

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
version = "20260226.0"
version = "20260128.0"
license = "Apache-2.0"
license-files = ["LICENSE*"]
description = "The Home Assistant frontend"

View File

@@ -31,18 +31,9 @@ type DialogSheetMode = "dialog" | "bottom-sheet";
* @slot footer - Dialog/sheet footer content.
*
* @cssprop --ha-dialog-surface-background - Dialog/sheet background color.
* @cssprop --ha-dialog-surface-backdrop-filter - Dialog/sheet backdrop filter.
* @cssprop --dialog-box-shadow - Dialog box shadow (dialog mode only).
* @cssprop --ha-dialog-border-radius - Border radius of the dialog surface (dialog mode only).
* @cssprop --ha-dialog-show-duration - Show animation duration (dialog mode only).
* @cssprop --ha-dialog-hide-duration - Hide animation duration (dialog mode only).
* @cssprop --ha-dialog-scrim-backdrop-filter - Dialog/sheet scrim backdrop filter.
* @cssprop --dialog-backdrop-filter - Dialog/sheet scrim backdrop filter (legacy).
* @cssprop --mdc-dialog-scrim-color - Dialog/sheet scrim color (legacy).
* @cssprop --ha-bottom-sheet-surface-background - Bottom sheet background color (sheet mode only).
* @cssprop --ha-bottom-sheet-surface-backdrop-filter - Bottom sheet backdrop filter (sheet mode only).
* @cssprop --ha-bottom-sheet-scrim-backdrop-filter - Bottom sheet scrim backdrop filter (sheet mode only).
* @cssprop --ha-bottom-sheet-scrim-color - Bottom sheet scrim color (sheet mode only).
*
* @attr {boolean} open - Controls the dialog/sheet open state.
* @attr {("alert"|"standard")} type - Dialog type (dialog mode only). Defaults to "standard".

View File

@@ -672,11 +672,11 @@ export class HaAssistChat extends LitElement {
--markdown-code-background-color: var(--primary-background-color);
--markdown-code-text-color: var(--primary-text-color);
--markdown-list-indent: 1.15em;
&:not(:has(ha-markdown-element)) {
min-height: 1lh;
min-width: 1lh;
flex-shrink: 0;
}
}
ha-markdown:not(:has(ha-markdown-element)) {
min-height: 1lh;
min-width: 1lh;
flex-shrink: 0;
}
.bouncer {
width: 48px;

View File

@@ -25,27 +25,6 @@ const SWIPE_LOCKED_COMPONENTS = new Set([
const SWIPE_LOCKED_CLASSES = new Set(["volume-slider-container", "forecast"]);
/**
* Home Assistant bottom sheet component.
*
* @element ha-bottom-sheet
* @extends {LitElement}
*
* @cssprop --ha-bottom-sheet-height - Preferred height of the bottom sheet.
* @cssprop --ha-bottom-sheet-max-height - Maximum height of the bottom sheet.
* @cssprop --ha-bottom-sheet-max-width - Maximum width of the bottom sheet.
* @cssprop --ha-bottom-sheet-border-radius - Top border radius of the bottom sheet.
* @cssprop --ha-bottom-sheet-surface-background - Bottom sheet background color.
* @cssprop --ha-bottom-sheet-surface-backdrop-filter - Bottom sheet surface backdrop filter.
* @cssprop --ha-bottom-sheet-scrim-backdrop-filter - Bottom sheet scrim backdrop filter.
* @cssprop --ha-bottom-sheet-scrim-color - Bottom sheet scrim color.
*
* @cssprop --ha-dialog-surface-background - Bottom sheet background color fallback.
* @cssprop --ha-dialog-surface-backdrop-filter - Bottom sheet surface backdrop filter fallback.
* @cssprop --ha-dialog-scrim-backdrop-filter - Bottom sheet scrim backdrop filter fallback.
* @cssprop --dialog-backdrop-filter - Bottom sheet scrim backdrop filter legacy fallback.
* @cssprop --mdc-dialog-scrim-color - Bottom sheet scrim color legacy fallback.
*/
@customElement("ha-bottom-sheet")
export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
@property({ attribute: false }) public hass?: HomeAssistant;
@@ -162,9 +141,6 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
private _handleKeyDown = (ev: KeyboardEvent) => {
if (ev.key === "Escape") {
this._escapePressed = true;
if (this.preventScrimClose) {
ev.preventDefault();
}
ev.stopPropagation();
(ev.currentTarget as WaDrawer).open = false;
}
@@ -406,26 +382,6 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
transform: var(--dialog-transform);
transition: var(--dialog-transition);
}
wa-drawer::part(dialog)::backdrop {
-webkit-backdrop-filter: var(
--ha-bottom-sheet-scrim-backdrop-filter,
var(
--ha-dialog-scrim-backdrop-filter,
var(--dialog-backdrop-filter, none)
)
);
backdrop-filter: var(
--ha-bottom-sheet-scrim-backdrop-filter,
var(
--ha-dialog-scrim-backdrop-filter,
var(--dialog-backdrop-filter, none)
)
);
background-color: var(
--ha-bottom-sheet-scrim-color,
var(--mdc-dialog-scrim-color, none)
);
}
wa-drawer::part(body) {
max-width: var(--ha-bottom-sheet-max-width);
width: 100%;
@@ -440,18 +396,7 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
);
background-color: var(
--ha-bottom-sheet-surface-background,
var(
--ha-dialog-surface-background,
var(--card-background-color, var(--ha-color-surface-default))
)
);
-webkit-backdrop-filter: var(
--ha-bottom-sheet-surface-backdrop-filter,
var(--ha-dialog-surface-backdrop-filter, none)
);
backdrop-filter: var(
--ha-bottom-sheet-surface-backdrop-filter,
var(--ha-dialog-surface-backdrop-filter, none)
var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff)),
);
padding: var(
--ha-bottom-sheet-padding,

View File

@@ -93,8 +93,6 @@ export class HaDateRangePicker extends LitElement {
| "center"
| "inline";
@state() private _calcedVerticalOpeningDirection?: "up" | "down";
protected willUpdate(changedProps: PropertyValues) {
if (
(!this.hasUpdated && this.ranges === undefined) ||
@@ -136,9 +134,7 @@ export class HaDateRangePicker extends LitElement {
opening-direction=${ifDefined(
this.openingDirection || this._calcedOpeningDirection
)}
opens-vertical=${ifDefined(
this.verticalOpeningDirection || this._calcedVerticalOpeningDirection
)}
opens-vertical=${ifDefined(this.verticalOpeningDirection)}
first-day=${firstWeekdayIndex(this.hass.locale)}
language=${this.hass.locale.language}
@change=${this._handleChange}
@@ -332,24 +328,17 @@ export class HaDateRangePicker extends LitElement {
private _handleClick() {
// calculate opening direction if not set
if (!this._dateRangePicker.open) {
if (!this.openingDirection) {
const datePickerPosition = this.getBoundingClientRect().x;
let opens: "right" | "left" | "center" | "inline";
if (datePickerPosition > (2 * window.innerWidth) / 3) {
opens = "left";
} else if (datePickerPosition < window.innerWidth / 3) {
opens = "right";
} else {
opens = "center";
}
this._calcedOpeningDirection = opens;
}
if (!this.verticalOpeningDirection) {
const rect = this.getBoundingClientRect();
this._calcedVerticalOpeningDirection =
rect.top > window.innerHeight / 2 ? "up" : "down";
if (!this._dateRangePicker.open && !this.openingDirection) {
const datePickerPosition = this.getBoundingClientRect().x;
let opens: "right" | "left" | "center" | "inline";
if (datePickerPosition > (2 * window.innerWidth) / 3) {
opens = "left";
} else if (datePickerPosition < window.innerWidth / 3) {
opens = "right";
} else {
opens = "center";
}
this._calcedOpeningDirection = opens;
}
}

View File

@@ -52,12 +52,7 @@ type DialogHideEvent = CustomEvent<{ source?: Element }>;
* @cssprop --ha-dialog-show-duration - Show animation duration.
* @cssprop --ha-dialog-hide-duration - Hide animation duration.
* @cssprop --ha-dialog-surface-background - Dialog background color.
* @cssprop --ha-dialog-surface-backdrop-filter - Dialog backdrop filter.
* @cssprop --dialog-box-shadow - Dialog box shadow.
* @cssprop --ha-dialog-border-radius - Border radius of the dialog surface.
* @cssprop --ha-dialog-scrim-backdrop-filter - Dialog scrim backdrop filter.
* @cssprop --dialog-backdrop-filter - Dialog scrim backdrop filter (legacy).
* @cssprop --mdc-dialog-scrim-color - Dialog scrim color (legacy).
* @cssprop --dialog-surface-margin-top - Top margin for the dialog surface.
*
* @attr {boolean} open - Controls the dialog open state.
@@ -244,9 +239,6 @@ export class HaDialog extends ScrollableFadeMixin(LitElement) {
private _handleKeyDown(ev: KeyboardEvent) {
if (ev.key === "Escape") {
this._escapePressed = true;
if (this.preventScrimClose) {
ev.preventDefault();
}
ev.stopPropagation();
(ev.currentTarget as WaDialog).open = false;
}
@@ -276,6 +268,10 @@ export class HaDialog extends ScrollableFadeMixin(LitElement) {
--spacing: var(--dialog-content-padding, var(--ha-space-6));
--show-duration: var(--ha-dialog-show-duration, 200ms);
--hide-duration: var(--ha-dialog-hide-duration, 200ms);
--ha-dialog-surface-background: var(
--card-background-color,
var(--ha-color-surface-default)
);
--wa-color-surface-raised: var(
--ha-dialog-surface-background,
var(--card-background-color, var(--ha-color-surface-default))
@@ -306,12 +302,6 @@ export class HaDialog extends ScrollableFadeMixin(LitElement) {
}
wa-dialog::part(dialog) {
-webkit-backdrop-filter: var(
--ha-dialog-surface-backdrop-filter,
none
);
backdrop-filter: var(--ha-dialog-surface-backdrop-filter, none);
box-shadow: var(--dialog-box-shadow, var(--wa-shadow-l));
color: var(--primary-text-color);
min-width: var(--width, var(--full-width));
max-width: var(--width, var(--full-width));
@@ -341,44 +331,32 @@ export class HaDialog extends ScrollableFadeMixin(LitElement) {
overflow: hidden;
}
wa-dialog::part(dialog)::backdrop {
-webkit-backdrop-filter: var(
--ha-dialog-scrim-backdrop-filter,
var(--dialog-backdrop-filter, none)
);
backdrop-filter: var(
--ha-dialog-scrim-backdrop-filter,
var(--dialog-backdrop-filter, none)
);
background-color: var(--mdc-dialog-scrim-color, none);
}
@media all and (max-width: 450px), all and (max-height: 500px) {
:host([type="standard"]) {
--ha-dialog-border-radius: 0;
}
wa-dialog {
/* Make the container fill the whole screen width and not the safe width */
--full-width: var(--ha-dialog-width-full, 100vw);
--width: var(--full-width);
}
:host([type="standard"]) wa-dialog {
/* Make the container fill the whole screen width and not the safe width */
--full-width: var(--ha-dialog-width-full, 100vw);
--width: var(--full-width);
}
wa-dialog::part(dialog) {
/* Make the dialog fill the whole screen height and not the safe height */
min-height: var(--ha-dialog-min-height, 100vh);
min-height: var(--ha-dialog-min-height, 100dvh);
max-height: var(--ha-dialog-max-height, 100vh);
max-height: var(--ha-dialog-max-height, 100dvh);
margin-top: 0;
margin-bottom: 0;
/* Use safe area as padding instead of the container size */
padding-top: var(--safe-area-inset-top);
padding-bottom: var(--safe-area-inset-bottom);
padding-left: var(--safe-area-inset-left);
padding-right: var(--safe-area-inset-right);
/* Reset the transform to center the dialog */
transform: none;
}
:host([type="standard"]) wa-dialog::part(dialog) {
/* Make the dialog fill the whole screen height and not the safe height */
min-height: var(--ha-dialog-min-height, 100vh);
min-height: var(--ha-dialog-min-height, 100dvh);
max-height: var(--ha-dialog-max-height, 100vh);
max-height: var(--ha-dialog-max-height, 100dvh);
margin-top: 0;
margin-bottom: 0;
/* Use safe area as padding instead of the container size */
padding-top: var(--safe-area-inset-top);
padding-bottom: var(--safe-area-inset-bottom);
padding-left: var(--safe-area-inset-left);
padding-right: var(--safe-area-inset-right);
/* Reset the transform to center the dialog */
transform: none;
}
}

View File

@@ -37,9 +37,6 @@ export class HaIconButtonToggle extends HaIconButton {
background-color: transparent;
border: 2px solid var(--primary-text-color);
}
:host([selected]) ha-button::after {
opacity: 0;
}
:host([selected]) ha-button::part(base) {
color: var(--primary-background-color);
background-color: unset;

472
src/components/ha-input.ts Normal file
View File

@@ -0,0 +1,472 @@
import { preventDefault } from "@fullcalendar/core/internal";
import "@home-assistant/webawesome/dist/components/animation/animation";
import "@home-assistant/webawesome/dist/components/input/input";
import type WaInput from "@home-assistant/webawesome/dist/components/input/input";
import { mdiClose, mdiEye, mdiEyeOff, mdiInformationOutline } from "@mdi/js";
import { LitElement, type PropertyValues, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import "./ha-icon-button";
import "./ha-svg-icon";
import "./ha-tooltip";
@customElement("ha-input")
export class HaInput extends LitElement {
/** The type of input. */
@property()
public type:
| "date"
| "datetime-local"
| "email"
| "number"
| "password"
| "search"
| "tel"
| "text"
| "time"
| "url" = "text";
/** The current value of the input. */
@property()
public value?: string;
/** The input's size. */
@property()
public size: "small" | "medium" | "large" = "medium";
/** The input's visual appearance. */
@property()
public appearance: "filled" | "outlined" | "filled-outlined" = "outlined";
/** Draws a pill-style input with rounded edges. */
@property({ type: Boolean })
public pill = false;
/** The input's label. */
@property()
public label = "";
/** The input's hint. */
@property()
public hint = "";
/** Adds a clear button when the input is not empty. */
@property({ type: Boolean, attribute: "with-clear" })
public withClear = false;
/** Placeholder text to show as a hint when the input is empty. */
@property()
public placeholder = "";
/** Makes the input readonly. */
@property({ type: Boolean })
public readonly = false;
/** Adds a button to toggle the password's visibility. */
@property({ type: Boolean, attribute: "password-toggle" })
public passwordToggle = false;
/** Determines whether or not the password is currently visible. */
@property({ type: Boolean, attribute: "password-visible" })
public passwordVisible = false;
/** Hides the browser's built-in increment/decrement spin buttons for number inputs. */
@property({ type: Boolean, attribute: "without-spin-buttons" })
public withoutSpinButtons = false;
/** Makes the input a required field. */
@property({ type: Boolean })
public required = false;
/** A regular expression pattern to validate input against. */
@property()
public pattern?: string;
/** The minimum length of input that will be considered valid. */
@property({ type: Number })
public minlength?: number;
/** The maximum length of input that will be considered valid. */
@property({ type: Number })
public maxlength?: number;
/** The input's minimum value. Only applies to date and number input types. */
@property()
public min?: number | string;
/** The input's maximum value. Only applies to date and number input types. */
@property()
public max?: number | string;
/** Specifies the granularity that the value must adhere to. */
@property()
public step?: number | "any";
/** Controls whether and how text input is automatically capitalized. */
@property()
// eslint-disable-next-line lit/no-native-attributes
public autocapitalize:
| "off"
| "none"
| "on"
| "sentences"
| "words"
| "characters"
| "" = "";
/** Indicates whether the browser's autocorrect feature is on or off. */
@property({ type: Boolean })
public autocorrect = false;
/** Specifies what permission the browser has to provide assistance in filling out form field values. */
@property()
public autocomplete?: string;
/** Indicates that the input should receive focus on page load. */
@property({ type: Boolean })
// eslint-disable-next-line lit/no-native-attributes
public autofocus = false;
/** Used to customize the label or icon of the Enter key on virtual keyboards. */
@property()
// eslint-disable-next-line lit/no-native-attributes
public enterkeyhint:
| "enter"
| "done"
| "go"
| "next"
| "previous"
| "search"
| "send"
| "" = "";
/** Enables spell checking on the input. */
@property({ type: Boolean })
// eslint-disable-next-line lit/no-native-attributes
public spellcheck = true;
/** Tells the browser what type of data will be entered by the user. */
@property()
// eslint-disable-next-line lit/no-native-attributes
public inputmode:
| "none"
| "text"
| "decimal"
| "numeric"
| "tel"
| "search"
| "email"
| "url"
| "" = "";
/** The name of the input, submitted as a name/value pair with form data. */
@property()
public name?: string;
/** Disables the form control. */
@property({ type: Boolean })
public disabled = false;
/** Custom validation message to show when the input is invalid. */
@property({ attribute: "validation-message" })
public validationMessage = "";
/** When true, validates the input on blur instead of on form submit. */
@property({ type: Boolean, attribute: "auto-validate" })
public autoValidate = false;
@property({ type: Boolean })
public invalid = false;
@state()
private _invalid = false;
@query("wa-input")
private _input?: WaInput;
static shadowRootOptions: ShadowRootInit = {
mode: "open",
delegatesFocus: true,
};
/** Selects all the text in the input. */
public select(): void {
this._input?.select();
}
/** Sets the start and end positions of the text selection (0-based). */
public setSelectionRange(
selectionStart: number,
selectionEnd: number,
selectionDirection?: "forward" | "backward" | "none"
): void {
this._input?.setSelectionRange(
selectionStart,
selectionEnd,
selectionDirection
);
}
/** Replaces a range of text with a new string. */
public setRangeText(
replacement: string,
start?: number,
end?: number,
selectMode?: "select" | "start" | "end" | "preserve"
): void {
this._input?.setRangeText(replacement, start, end, selectMode);
}
/** Displays the browser picker for an input element. */
public showPicker(): void {
this._input?.showPicker();
}
/** Increments the value of a numeric input type by the value of the step attribute. */
public stepUp(): void {
this._input?.stepUp();
}
/** Decrements the value of a numeric input type by the value of the step attribute. */
public stepDown(): void {
this._input?.stepDown();
}
public checkValidity(): boolean {
return this._input?.checkValidity() ?? true;
}
public reportValidity(): boolean {
const valid = this.checkValidity();
this._invalid = !valid;
return valid;
}
protected override updated(changedProperties: PropertyValues): void {
super.updated(changedProperties);
const nativeInput = this._input?.input;
if (!nativeInput) return;
// wa-input hardcodes aria-describedby="hint" pointing to its internal hint slot wrapper.
// We remove it and use aria-description instead to properly convey our hint or error text.
// TODO: fix upstream in wa-input
nativeInput.removeAttribute("aria-describedby");
// wa-input doesn't set aria-invalid on its internal <input>, so we do it manually
// TODO: fix upstream in wa-input
if (changedProperties.has("invalid") || changedProperties.has("_invalid")) {
const isInvalid = this.invalid || this._invalid;
nativeInput.setAttribute("aria-invalid", String(isInvalid));
}
// Expose hint or validation error to screen readers on the input itself
const description =
this.invalid || this._invalid
? this.validationMessage || this._input?.validationMessage
: this.hint;
if (description) {
nativeInput.setAttribute("aria-description", description);
} else {
nativeInput.removeAttribute("aria-description");
}
}
protected render() {
return html`
<wa-input
.type=${this.type}
.value=${this.value ?? null}
.size=${this.size}
.appearance=${this.appearance}
.withClear=${this.withClear}
.placeholder=${this.placeholder}
.readonly=${this.readonly}
.passwordToggle=${this.passwordToggle}
.passwordVisible=${this.passwordVisible}
.withoutSpinButtons=${this.withoutSpinButtons}
.required=${this.required}
.pattern=${this.pattern}
.minlength=${this.minlength}
.maxlength=${this.maxlength}
.min=${this.min}
.max=${this.max}
.step=${this.step}
.autocapitalize=${this.autocapitalize || undefined}
.autocorrect=${this.autocorrect ? "on" : "off"}
.autocomplete=${this.autocomplete}
.autofocus=${this.autofocus}
.enterkeyhint=${this.enterkeyhint || undefined}
.spellcheck=${this.spellcheck}
.inputmode=${this.inputmode || undefined}
.name=${this.name}
.disabled=${this.disabled}
class=${this.invalid || this._invalid ? "invalid" : ""}
@input=${this._handleInput}
@change=${this._handleChange}
@blur=${this._handleBlur}
@wa-invalid=${this._handleInvalid}
>
<div class="label" slot="label">
<span>
<slot name="label">${this.label}</slot>
</span>
${this.hint
? html`<ha-icon-button
@click=${preventDefault}
.path=${mdiInformationOutline}
.label=${"Hint"}
hide-title
id="hint"
></ha-icon-button>
<ha-tooltip for="hint">${this.hint}</ha-tooltip> `
: nothing}
</div>
<slot name="start" slot="start"></slot>
<slot name="end" slot="end"></slot>
<slot name="clear-icon" slot="clear-icon">
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</slot>
<slot name="show-password-icon" slot="show-password-icon">
<ha-svg-icon .path=${mdiEye}></ha-svg-icon>
</slot>
<slot name="hide-password-icon" slot="hide-password-icon">
<ha-svg-icon .path=${mdiEyeOff}></ha-svg-icon>
</slot>
<div
slot="hint"
class="error ${this.invalid || this._invalid ? "visible" : ""}"
role="alert"
aria-live="assertive"
>
<span
>${this.validationMessage || this._input?.validationMessage}</span
>
</div>
</wa-input>
`;
}
private _handleInput() {
this.value = this._input?.value ?? undefined;
if (this._invalid && this._input?.checkValidity()) {
this._invalid = false;
}
}
private _handleChange() {
this.value = this._input?.value ?? undefined;
}
private _handleBlur() {
if (this.autoValidate) {
this._invalid = !this._input?.checkValidity();
}
}
private _handleInvalid() {
this._invalid = true;
}
static styles = css`
:host {
display: flex;
align-items: flex-start;
padding-top: var(--ha-input-padding-top, var(--ha-space-2));
padding-bottom: var(--ha-input-padding-bottom, var(--ha-space-2));
}
wa-input {
flex: 1;
min-width: 0;
}
wa-input::part(base):focus-within {
outline: none;
--wa-form-control-border-color: var(--ha-color-border-primary-normal);
}
wa-input.invalid,
wa-input.invalid::part(base):focus-within {
--wa-form-control-border-color: var(--ha-color-border-danger-normal);
}
wa-input::part(label) {
margin-block-end: 2px;
}
.label {
height: 24px;
display: flex;
width: 100%;
align-items: center;
color: var(--ha-color-text-secondary);
font-size: var(--ha-font-size-s);
font-weight: var(--ha-font-weight-medium);
gap: var(--ha-space-1);
}
.label span {
line-height: 1;
flex: 1;
min-width: 0;
overflow-x: clip;
overflow-y: visible;
text-overflow: ellipsis;
white-space: nowrap;
}
.label ha-svg-icon {
color: var(--ha-color-on-disabled-normal);
--mdc-icon-size: 16px;
}
#hint {
--ha-icon-button-size: 16px;
--mdc-icon-size: 16px;
color: var(--ha-color-on-disabled-normal);
}
wa-input::part(hint) {
margin-block-start: 0;
color: var(--ha-color-on-danger-quiet);
font-size: var(--ha-font-size-s);
margin-inline-start: var(--ha-space-3);
}
.error {
padding-top: var(--ha-space-1);
transition:
opacity 0.3s ease-out,
height 0.3s ease-out;
height: 0;
overflow: hidden;
}
.error span {
transition: transform 0.3s ease-out;
display: inline-block;
transform: translateY(
calc(-1 * (var(--ha-font-size-s) + var(--ha-space-1)))
);
}
.error.visible {
height: calc(var(--ha-font-size-s) + var(--ha-space-2));
}
.error.visible span {
transform: translateY(0);
}
wa-input::part(end) {
color: var(--ha-color-text-secondary);
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-input": HaInput;
}
}

View File

@@ -84,13 +84,11 @@ export class HaMarkdown extends LitElement {
ha-markdown-element > :is(ol, ul) {
padding-inline-start: var(--markdown-list-indent, revert);
}
li {
&:has(input[type="checkbox"]) {
list-style: none;
& > input[type="checkbox"] {
margin-left: 0;
}
}
li:has(input[type="checkbox"]) {
list-style: none;
}
li:has(input[type="checkbox"]) > input[type="checkbox"] {
margin-left: 0;
}
svg {
background-color: var(--markdown-svg-background-color, none);
@@ -137,10 +135,10 @@ export class HaMarkdown extends LitElement {
--markdown-table-border-width: 0;
--markdown-table-padding-inline: 0;
--markdown-table-padding-block: 0;
th,
td {
vertical-align: middle;
}
}
table[role="presentation"] th,
table[role="presentation"] td {
vertical-align: middle;
}
table[role="presentation"] td[valign="top"],
table[role="presentation"] th[valign="top"] {

View File

@@ -1,34 +0,0 @@
import { styles } from "@material/web/textfield/internal/filled-styles";
import { FilledTextField } from "@material/web/textfield/internal/filled-text-field";
import { styles as sharedStyles } from "@material/web/textfield/internal/shared-styles";
import { css } from "lit";
import { customElement } from "lit/decorators";
@customElement("ha-md-textfield")
export class HaMdTextfield extends FilledTextField {
static override styles = [
sharedStyles,
styles,
css`
:host {
--ha-icon-display: block;
--md-sys-color-primary: var(--primary-text-color);
--md-sys-color-secondary: var(--secondary-text-color);
--md-sys-color-surface: var(--card-background-color);
--md-sys-color-on-surface-variant: var(--secondary-text-color);
--md-sys-color-surface-container-highest: var(--input-fill-color);
--md-sys-color-on-surface: var(--input-ink-color);
--md-sys-color-surface-container: var(--input-fill-color);
--md-sys-color-secondary-container: var(--input-fill-color);
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"ha-md-textfield": HaMdTextfield;
}
}

View File

@@ -1,211 +0,0 @@
import type { TextAreaCharCounter } from "@material/mwc-textfield/mwc-textfield-base";
import { mdiEye, mdiEyeOff } from "@mdi/js";
import { LitElement, css, html } from "lit";
import {
customElement,
eventOptions,
property,
query,
state,
} from "lit/decorators";
import type { HomeAssistant } from "../types";
import "./ha-icon-button";
import "./ha-textfield";
import type { HaTextField } from "./ha-textfield";
@customElement("ha-password-field")
export class HaPasswordField extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public invalid?: boolean;
@property({ attribute: "error-message" }) public errorMessage?: string;
@property({ type: Boolean }) public icon = false;
// eslint-disable-next-line lit/attribute-names
@property({ type: Boolean }) public iconTrailing = false;
@property() public autocomplete?: string;
@property({ type: Boolean }) public autocorrect = true;
@property({ attribute: "input-spellcheck" })
public inputSpellcheck?: string;
@property({ type: String }) value = "";
@property({ type: String }) placeholder = "";
@property({ type: String }) label = "";
@property({ type: Boolean, reflect: true }) disabled = false;
@property({ type: Boolean }) required = false;
// eslint-disable-next-line lit/attribute-names
@property({ type: Number }) minLength = -1;
// eslint-disable-next-line lit/attribute-names
@property({ type: Number }) maxLength = -1;
@property({ type: Boolean, reflect: true }) outlined = false;
@property({ type: String }) helper = "";
// eslint-disable-next-line lit/attribute-names
@property({ type: Boolean }) validateOnInitialRender = false;
// eslint-disable-next-line lit/attribute-names
@property({ type: String }) validationMessage = "";
// eslint-disable-next-line lit/attribute-names
@property({ type: Boolean }) autoValidate = false;
@property({ type: String }) pattern = "";
@property({ type: Number }) size: number | null = null;
// eslint-disable-next-line lit/attribute-names
@property({ type: Boolean }) helperPersistent = false;
// eslint-disable-next-line lit/attribute-names
@property({ type: Boolean }) charCounter: boolean | TextAreaCharCounter =
false;
// eslint-disable-next-line lit/attribute-names
@property({ type: Boolean }) endAligned = false;
@property({ type: String }) prefix = "";
@property({ type: String }) suffix = "";
@property({ type: String }) name = "";
@property({ type: String, attribute: "input-mode" })
inputMode!: string;
// eslint-disable-next-line lit/attribute-names
@property({ type: Boolean }) readOnly = false;
// eslint-disable-next-line lit/no-native-attributes
@property({ attribute: false }) autocapitalize = "";
@state() private _unmaskedPassword = false;
@query("ha-textfield") private _textField!: HaTextField;
protected render() {
return html`<ha-textfield
.invalid=${this.invalid}
.errorMessage=${this.errorMessage}
.icon=${this.icon}
.iconTrailing=${this.iconTrailing}
.autocomplete=${this.autocomplete}
.autocorrect=${this.autocorrect}
.inputSpellcheck=${this.inputSpellcheck}
.value=${this.value}
.placeholder=${this.placeholder}
.label=${this.label}
.disabled=${this.disabled}
.required=${this.required}
.minLength=${this.minLength}
.maxLength=${this.maxLength}
.outlined=${this.outlined}
.helper=${this.helper}
.validateOnInitialRender=${this.validateOnInitialRender}
.validationMessage=${this.validationMessage}
.autoValidate=${this.autoValidate}
.pattern=${this.pattern}
.size=${this.size}
.helperPersistent=${this.helperPersistent}
.charCounter=${this.charCounter}
.endAligned=${this.endAligned}
.prefix=${this.prefix}
.name=${this.name}
.inputMode=${this.inputMode}
.readOnly=${this.readOnly}
.autocapitalize=${this.autocapitalize}
.type=${this._unmaskedPassword ? "text" : "password"}
.suffix=${html`<div style="width: 24px"></div>`}
@input=${this._handleInputEvent}
@change=${this._handleChangeEvent}
></ha-textfield>
<ha-icon-button
.label=${this.hass?.localize(
this._unmaskedPassword
? "ui.components.selectors.text.hide_password"
: "ui.components.selectors.text.show_password"
) || (this._unmaskedPassword ? "Hide password" : "Show password")}
@click=${this._toggleUnmaskedPassword}
.path=${this._unmaskedPassword ? mdiEyeOff : mdiEye}
></ha-icon-button>`;
}
public focus(): void {
this._textField.focus();
}
public checkValidity(): boolean {
return this._textField.checkValidity();
}
public reportValidity(): boolean {
return this._textField.reportValidity();
}
public setCustomValidity(message: string): void {
return this._textField.setCustomValidity(message);
}
public layout(): Promise<void> {
return this._textField.layout();
}
private _toggleUnmaskedPassword(): void {
this._unmaskedPassword = !this._unmaskedPassword;
}
@eventOptions({ passive: true })
private _handleInputEvent(ev) {
this.value = ev.target.value;
}
@eventOptions({ passive: true })
private _handleChangeEvent(ev) {
this.value = ev.target.value;
this._reDispatchEvent(ev);
}
private _reDispatchEvent(oldEvent: Event) {
const newEvent = new Event(oldEvent.type, oldEvent);
this.dispatchEvent(newEvent);
}
static styles = css`
:host {
display: block;
position: relative;
}
ha-textfield {
width: 100%;
}
ha-icon-button {
position: absolute;
top: 8px;
right: 8px;
inset-inline-start: initial;
inset-inline-end: 8px;
--ha-icon-button-size: 40px;
--mdc-icon-size: 20px;
color: var(--secondary-text-color);
direction: var(--direction);
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-password-field": HaPasswordField;
}
}

View File

@@ -1,242 +1,351 @@
import { TextFieldBase } from "@material/mwc-textfield/mwc-textfield-base";
import { styles } from "@material/mwc-textfield/mwc-textfield.css";
import type { TemplateResult, PropertyValues } from "lit";
import { html, css } from "lit";
import type { PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query } from "lit/decorators";
import { mainWindow } from "../common/dom/get_main_window";
import "./ha-input";
import type { HaInput } from "./ha-input";
/**
* Legacy wrapper around ha-input that preserves the mwc-textfield API.
* New code should use ha-input directly.
* @deprecated Use ha-input instead.
*/
@customElement("ha-textfield")
export class HaTextField extends TextFieldBase {
@property({ type: Boolean }) public invalid?: boolean;
export class HaTextField extends LitElement {
@property({ type: String })
public value = "";
@property({ attribute: "error-message" }) public errorMessage?: string;
@property({ type: String })
public type:
| "text"
| "search"
| "tel"
| "url"
| "email"
| "password"
| "date"
| "month"
| "week"
| "time"
| "datetime-local"
| "number"
| "color" = "text";
@property({ type: String })
public label = "";
@property({ type: String })
public placeholder = "";
@property({ type: String })
public prefix = "";
@property({ type: String })
public suffix = "";
@property({ type: Boolean })
// @ts-ignore
@property({ type: Boolean }) public icon = false;
public icon = false;
@property({ type: Boolean })
// @ts-ignore
// eslint-disable-next-line lit/attribute-names
@property({ type: Boolean }) public iconTrailing = false;
public iconTrailing = false;
@property() public autocomplete?: string;
@property({ type: Boolean })
public disabled = false;
@property({ type: Boolean }) public autocorrect = true;
@property({ type: Boolean })
public required = false;
@property({ type: Number, attribute: "minlength" })
public minLength = -1;
@property({ type: Number, attribute: "maxlength" })
public maxLength = -1;
@property({ type: Boolean, reflect: true })
public outlined = false;
@property({ type: String })
public helper = "";
@property({ type: Boolean, attribute: "validateoninitialrender" })
public validateOnInitialRender = false;
@property({ type: String, attribute: "validationmessage" })
public validationMessage = "";
@property({ type: Boolean, attribute: "autovalidate" })
public autoValidate = false;
@property({ type: String })
public pattern = "";
@property()
public min: number | string = "";
@property()
public max: number | string = "";
@property()
public step: number | "any" | null = null;
@property({ type: Number })
public size: number | null = null;
@property({ type: Boolean, attribute: "helperpersistent" })
public helperPersistent = false;
@property({ attribute: "charcounter" })
public charCounter: boolean | "external" | "internal" = false;
@property({ type: Boolean, attribute: "endaligned" })
public endAligned = false;
@property({ type: String, attribute: "inputmode" })
public inputMode = "";
@property({ type: Boolean, reflect: true, attribute: "readonly" })
public readOnly = false;
@property({ type: String })
public name = "";
@property({ type: String })
// eslint-disable-next-line lit/no-native-attributes
public autocapitalize = "";
// --- ha-textfield-specific properties ---
@property({ type: Boolean })
public invalid = false;
@property({ attribute: "error-message" })
public errorMessage?: string;
@property()
public autocomplete?: string;
@property({ type: Boolean })
public autocorrect = true;
@property({ attribute: "input-spellcheck" })
public inputSpellcheck?: string;
@query("input") public formElement!: HTMLInputElement;
@query("ha-input")
private _haInput?: HaInput;
override updated(changedProperties: PropertyValues) {
static shadowRootOptions: ShadowRootInit = {
mode: "open",
delegatesFocus: true,
};
public get formElement(): HTMLInputElement | undefined {
return (this._haInput as any)?._input?.input;
}
public select(): void {
this._haInput?.select();
}
public setSelectionRange(
selectionStart: number,
selectionEnd: number,
selectionDirection?: "forward" | "backward" | "none"
): void {
this._haInput?.setSelectionRange(
selectionStart,
selectionEnd,
selectionDirection
);
}
public setRangeText(
replacement: string,
start?: number,
end?: number,
selectMode?: "select" | "start" | "end" | "preserve"
): void {
this._haInput?.setRangeText(replacement, start, end, selectMode);
}
public checkValidity(): boolean {
return this._haInput?.checkValidity() ?? true;
}
public reportValidity(): boolean {
return this._haInput?.reportValidity() ?? true;
}
public setCustomValidity(message: string): void {
this.validationMessage = message;
this.invalid = !!message;
}
/** No-op. Preserved for backward compatibility. */
public layout(): void {
// no-op — mwc-textfield needed this for notched outline recalculation
}
protected override firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
if (this.validateOnInitialRender) {
this.reportValidity();
}
}
protected override updated(changedProperties: PropertyValues): void {
super.updated(changedProperties);
if (
changedProperties.has("invalid") ||
changedProperties.has("errorMessage")
) {
this.setCustomValidity(
this.invalid
? this.errorMessage || this.validationMessage || "Invalid"
: ""
);
if (changedProperties.has("invalid") && this._haInput) {
if (
this.invalid ||
this.validateOnInitialRender ||
(changedProperties.has("invalid") &&
changedProperties.get("invalid") !== undefined)
(changedProperties.get("invalid") !== undefined && !this.invalid)
) {
// Only report validity if the field is invalid or the invalid state has changed from
// true to false to prevent setting empty required fields to invalid on first render
this.reportValidity();
}
}
if (changedProperties.has("autocomplete")) {
if (this.autocomplete) {
this.formElement.setAttribute("autocomplete", this.autocomplete);
} else {
this.formElement.removeAttribute("autocomplete");
}
}
if (changedProperties.has("autocorrect")) {
if (this.autocorrect === false) {
this.formElement.setAttribute("autocorrect", "off");
} else {
this.formElement.removeAttribute("autocorrect");
}
}
if (changedProperties.has("inputSpellcheck")) {
if (this.inputSpellcheck) {
this.formElement.setAttribute("spellcheck", this.inputSpellcheck);
} else {
this.formElement.removeAttribute("spellcheck");
}
}
private _mapType(
type: string
):
| "text"
| "search"
| "tel"
| "url"
| "email"
| "password"
| "date"
| "datetime-local"
| "number"
| "time" {
// mwc-textfield supports "color", "month", "week" which ha-input doesn't
switch (type) {
case "text":
case "search":
case "tel":
case "url":
case "email":
case "password":
case "date":
case "datetime-local":
case "number":
case "time":
return type;
default:
return "text";
}
}
protected override renderIcon(
_icon: string,
isTrailingIcon = false
): TemplateResult {
const type = isTrailingIcon ? "trailing" : "leading";
protected override render(): TemplateResult {
const errorMsg = this.errorMessage || this.validationMessage;
return html`
<span
class="mdc-text-field__icon mdc-text-field__icon--${type}"
tabindex=${isTrailingIcon ? 1 : -1}
<ha-input
.type=${this._mapType(this.type)}
.value=${this.value || undefined}
.label=${this.label}
.placeholder=${this.placeholder}
.disabled=${this.disabled}
.required=${this.required}
.readonly=${this.readOnly}
.pattern=${this.pattern || undefined}
.minlength=${this.minLength > 0 ? this.minLength : undefined}
.maxlength=${this.maxLength > 0 ? this.maxLength : undefined}
.min=${this.min !== "" ? this.min : undefined}
.max=${this.max !== "" ? this.max : undefined}
.step=${this.step ?? undefined}
.name=${this.name || undefined}
.autocomplete=${this.autocomplete}
.autocorrect=${this.autocorrect}
.spellcheck=${this.inputSpellcheck === "true"}
.inputmode=${this._mapInputMode(this.inputMode)}
.autocapitalize=${this.autocapitalize || ""}
.invalid=${this.invalid}
.validationMessage=${errorMsg || ""}
.autoValidate=${this.autoValidate}
.hint=${this.helper}
.withoutSpinButtons=${this.type === "number"}
@input=${this._onInput}
@change=${this._onChange}
>
<slot name="${type}Icon"></slot>
</span>
${this.icon
? html`<slot name="leadingIcon" slot="start"></slot>`
: nothing}
${this.prefix
? html`<span class="prefix" slot="start">${this.prefix}</span>`
: nothing}
${this.suffix
? html`<span class="suffix" slot="end">${this.suffix}</span>`
: nothing}
${this.iconTrailing
? html`<slot name="trailingIcon" slot="end"></slot>`
: nothing}
</ha-input>
`;
}
static override styles = [
styles,
css`
.mdc-text-field__input {
width: var(--ha-textfield-input-width, 100%);
}
.mdc-text-field:not(.mdc-text-field--with-leading-icon) {
padding: var(--text-field-padding, 0px 16px);
}
.mdc-text-field__affix--suffix {
padding-left: var(--text-field-suffix-padding-left, 12px);
padding-right: var(--text-field-suffix-padding-right, 0px);
padding-inline-start: var(--text-field-suffix-padding-left, 12px);
padding-inline-end: var(--text-field-suffix-padding-right, 0px);
direction: ltr;
}
.mdc-text-field--with-leading-icon {
padding-inline-start: var(--text-field-suffix-padding-left, 0px);
padding-inline-end: var(--text-field-suffix-padding-right, 16px);
direction: var(--direction);
}
private _mapInputMode(
mode: string
):
| "none"
| "text"
| "decimal"
| "numeric"
| "tel"
| "search"
| "email"
| "url"
| "" {
switch (mode) {
case "none":
case "text":
case "decimal":
case "numeric":
case "tel":
case "search":
case "email":
case "url":
return mode;
default:
return "";
}
}
.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon {
padding-left: var(--text-field-suffix-padding-left, 0px);
padding-right: var(--text-field-suffix-padding-right, 0px);
padding-inline-start: var(--text-field-suffix-padding-left, 0px);
padding-inline-end: 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);
}
private _onInput(): void {
this.value = this._haInput?.value ?? "";
}
.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__icon {
color: var(--secondary-text-color);
}
private _onChange(): void {
this.value = this._haInput?.value ?? "";
}
.mdc-text-field__icon--leading {
margin-inline-start: 16px;
margin-inline-end: 8px;
direction: var(--direction);
}
static override styles = css`
:host {
display: inline-flex;
flex-direction: column;
outline: none;
}
.mdc-text-field__icon--trailing {
padding: var(--textfield-icon-trailing-padding, 12px);
}
ha-input {
--ha-input-padding-bottom: 0;
width: 100%;
}
.mdc-floating-label:not(.mdc-floating-label--float-above) {
max-width: calc(100% - 16px);
}
.prefix,
.suffix {
color: var(--secondary-text-color);
}
.mdc-floating-label--float-above {
max-width: calc((100% - 16px) / 0.75);
transition: none;
}
.prefix {
margin-inline-end: var(--text-field-prefix-padding-right);
}
input {
text-align: var(--text-field-text-align, start);
}
input[type="color"] {
height: 20px;
}
/* Edge, hide reveal password icon */
::-ms-reveal {
display: none;
}
/* Chrome, Safari, Edge, Opera */
:host([no-spinner]) input::-webkit-outer-spin-button,
:host([no-spinner]) input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="color"]::-webkit-color-swatch-wrapper {
padding: 0;
}
/* Firefox */
:host([no-spinner]) input[type="number"] {
-moz-appearance: textfield;
}
.mdc-text-field__ripple {
overflow: hidden;
}
.mdc-text-field {
overflow: var(--text-field-overflow);
}
.mdc-floating-label {
padding-inline-end: 16px;
padding-inline-start: initial;
inset-inline-start: 16px !important;
inset-inline-end: initial !important;
transform-origin: var(--float-start);
direction: var(--direction);
text-align: var(--float-start);
box-sizing: border-box;
text-overflow: ellipsis;
}
.mdc-text-field--with-leading-icon.mdc-text-field--filled
.mdc-floating-label {
max-width: calc(
100% - 48px - var(--text-field-suffix-padding-left, 0px)
);
inset-inline-start: calc(
48px + var(--text-field-suffix-padding-left, 0px)
) !important;
inset-inline-end: initial !important;
direction: var(--direction);
}
.mdc-text-field__input[type="number"] {
direction: var(--direction);
}
.mdc-text-field__affix--prefix {
padding-right: var(--text-field-prefix-padding-right, 2px);
padding-inline-end: var(--text-field-prefix-padding-right, 2px);
padding-inline-start: initial;
}
.mdc-text-field:not(.mdc-text-field--disabled)
.mdc-text-field__affix--prefix {
color: var(--mdc-text-field-label-ink-color);
}
#helper-text ha-markdown {
display: inline-block;
}
`,
// safari workaround - must be explicit
mainWindow.document.dir === "rtl"
? css`
.mdc-text-field--with-leading-icon,
.mdc-text-field__icon--leading,
.mdc-floating-label,
.mdc-text-field--with-leading-icon.mdc-text-field--filled
.mdc-floating-label,
.mdc-text-field__input[type="number"] {
direction: rtl;
--direction: rtl;
}
`
: css``,
];
/* Edge, hide reveal password icon */
::-ms-reveal {
display: none;
}
`;
}
declare global {

View File

@@ -288,7 +288,7 @@ export class QuickBar extends LitElement {
<ha-combo-box-item
tabindex="-1"
type="button"
style="--mdc-icon-size: 24px;"
style="--mdc-icon-size: 32px;"
>
${"stateObj" in item && item.stateObj
? html`
@@ -302,7 +302,6 @@ export class QuickBar extends LitElement {
? html`
<ha-domain-icon
slot="start"
style="margin: var(--ha-space-1);"
.hass=${this.hass}
.domain=${item.domain}
brand-fallback
@@ -320,11 +319,7 @@ export class QuickBar extends LitElement {
/>
`
: item.icon
? html`<ha-icon
style="margin: var(--ha-space-1);"
slot="start"
.icon=${item.icon}
></ha-icon>`
? html`<ha-icon slot="start" .icon=${item.icon}></ha-icon>`
: "iconColor" in item && item.iconColor
? html`
<div
@@ -338,11 +333,7 @@ export class QuickBar extends LitElement {
</div>
`
: html`
<ha-svg-icon
style="margin: var(--ha-space-1);"
slot="start"
.path=${iconPath}
></ha-svg-icon>
<ha-svg-icon slot="start" .path=${iconPath}></ha-svg-icon>
`}
<span slot="headline">${item.primary}</span>
${item.secondary

View File

@@ -4,11 +4,9 @@ import { fireEvent } from "../../../common/dom/fire_event";
import { navigate } from "../../../common/navigate";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-password-field";
import type { HaPasswordField } from "../../../components/ha-password-field";
import "../../../components/ha-input";
import type { HaInput } from "../../../components/ha-input";
import "../../../components/ha-svg-icon";
import "../../../components/ha-textfield";
import type { HaTextField } from "../../../components/ha-textfield";
import { cloudLogin } from "../../../data/cloud";
import { showCloudAlreadyConnectedDialog } from "../../../panels/config/cloud/dialog-cloud-already-connected/show-dialog-cloud-already-connected";
import type { HomeAssistant } from "../../../types";
@@ -28,9 +26,9 @@ export class CloudStepSignin extends LitElement {
@state() private _checkConnection = true;
@query("#email", true) private _emailField!: HaTextField;
@query("#email", true) private _emailField!: HaInput;
@query("#password", true) private _passwordField!: HaPasswordField;
@query("#password", true) private _passwordField!: HaInput;
render() {
return html`<div class="content">
@@ -42,7 +40,7 @@ export class CloudStepSignin extends LitElement {
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: ""}
<ha-textfield
<ha-input
autofocus
id="email"
name="email"
@@ -54,12 +52,14 @@ export class CloudStepSignin extends LitElement {
autocomplete="email"
required
@keydown=${this._keyDown}
validationMessage=${this.hass.localize(
.validationMessage=${this.hass.localize(
"ui.panel.config.cloud.register.email_error_msg"
)}
></ha-textfield>
<ha-password-field
></ha-input>
<ha-input
id="password"
type="password"
password-toggle
name="password"
.label=${this.hass.localize(
"ui.panel.config.cloud.register.password"
@@ -69,10 +69,10 @@ export class CloudStepSignin extends LitElement {
minlength="8"
required
@keydown=${this._keyDown}
validationMessage=${this.hass.localize(
.validationMessage=${this.hass.localize(
"ui.panel.config.cloud.register.password_error_msg"
)}
></ha-password-field>
></ha-input>
</div>
<div class="footer">
<ha-button
@@ -95,8 +95,8 @@ export class CloudStepSignin extends LitElement {
const emailField = this._emailField;
const passwordField = this._passwordField;
const email = emailField.value;
const password = passwordField.value;
const email = emailField.value as string;
const password = passwordField.value as string;
if (!emailField.reportValidity()) {
passwordField.reportValidity();
@@ -216,8 +216,7 @@ export class CloudStepSignin extends LitElement {
:host {
display: block;
}
ha-textfield,
ha-password-field {
ha-textfield {
display: block;
}
`,

View File

@@ -3,11 +3,9 @@ import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-password-field";
import type { HaPasswordField } from "../../../components/ha-password-field";
import "../../../components/ha-input";
import type { HaInput } from "../../../components/ha-input";
import "../../../components/ha-svg-icon";
import "../../../components/ha-textfield";
import type { HaTextField } from "../../../components/ha-textfield";
import {
cloudLogin,
cloudRegister,
@@ -30,9 +28,9 @@ export class CloudStepSignup extends LitElement {
@state() private _state?: "VERIFY";
@query("#email", true) private _emailField!: HaTextField;
@query("#email", true) private _emailField!: HaInput;
@query("#password", true) private _passwordField!: HaPasswordField;
@query("#password", true) private _passwordField!: HaInput;
render() {
return html`<div class="content">
@@ -53,7 +51,7 @@ export class CloudStepSignup extends LitElement {
{ email: this._email }
)}
</p>`
: html`<ha-textfield
: html`<ha-input
autofocus
id="email"
name="email"
@@ -65,12 +63,14 @@ export class CloudStepSignup extends LitElement {
autocomplete="email"
required
@keydown=${this._keyDown}
validationMessage=${this.hass.localize(
.validationMessage=${this.hass.localize(
"ui.panel.config.cloud.register.email_error_msg"
)}
></ha-textfield>
<ha-password-field
></ha-input>
<ha-input
id="password"
type="password"
password-toggle
name="password"
.label=${this.hass.localize(
"ui.panel.config.cloud.register.password"
@@ -80,10 +80,10 @@ export class CloudStepSignup extends LitElement {
minlength="8"
required
@keydown=${this._keyDown}
validationMessage=${this.hass.localize(
.validationMessage=${this.hass.localize(
"ui.panel.config.cloud.register.password_error_msg"
)}
></ha-password-field>`}
></ha-input>`}
</div>
<div class="footer side-by-side">
${this._state === "VERIFY"
@@ -131,19 +131,26 @@ export class CloudStepSignup extends LitElement {
const emailField = this._emailField;
const passwordField = this._passwordField;
let invalid = false;
if (!emailField.reportValidity()) {
passwordField.reportValidity();
invalid = true;
emailField.focus();
return;
}
if (!passwordField.reportValidity()) {
passwordField.focus();
if (!invalid) {
passwordField.focus();
}
invalid = true;
}
if (invalid) {
return;
}
const email = emailField.value.toLowerCase();
const password = passwordField.value;
const email = emailField.value!.toLowerCase();
const password = passwordField.value!;
this._requestInProgress = true;
@@ -211,10 +218,6 @@ export class CloudStepSignup extends LitElement {
.content {
width: 100%;
}
ha-textfield,
ha-password-field {
display: block;
}
`,
];
}

View File

@@ -1,25 +1,25 @@
import { css, html, LitElement, nothing, type CSSResultGroup } from "lit";
import { customElement, property, state, query } from "lit/decorators";
import "../../components/ha-button";
import { customElement, property, query, state } from "lit/decorators";
import { formatDateTimeWithBrowserDefaults } from "../../common/datetime/format_date_time";
import { fireEvent } from "../../common/dom/fire_event";
import type { LocalizeFunc } from "../../common/translations/localize";
import "../../components/buttons/ha-progress-button";
import type { HaProgressButton } from "../../components/buttons/ha-progress-button";
import "../../components/ha-alert";
import "../../components/ha-button";
import "../../components/ha-icon-button-arrow-prev";
import "../../components/ha-input";
import "../../components/ha-md-list";
import "../../components/ha-md-list-item";
import "../../components/buttons/ha-progress-button";
import "../../components/ha-icon-button-arrow-prev";
import "../../components/ha-password-field";
import "../../panels/config/backup/components/ha-backup-data-picker";
import "../../panels/config/backup/components/ha-backup-formfield-label";
import type { LocalizeFunc } from "../../common/translations/localize";
import {
getPreferredAgentForDownload,
type BackupContentExtended,
type BackupData,
} from "../../data/backup";
import { restoreOnboardingBackup } from "../../data/backup_onboarding";
import type { HaProgressButton } from "../../components/buttons/ha-progress-button";
import { fireEvent } from "../../common/dom/fire_event";
import "../../panels/config/backup/components/ha-backup-data-picker";
import "../../panels/config/backup/components/ha-backup-formfield-label";
import { onBoardingStyles } from "../styles";
import { formatDateTimeWithBrowserDefaults } from "../../common/datetime/format_date_time";
@customElement("onboarding-restore-backup-restore")
class OnboardingRestoreBackupRestore extends LitElement {
@@ -170,7 +170,7 @@ class OnboardingRestoreBackupRestore extends LitElement {
`ui.panel.page-onboarding.restore.details.restore.encryption.description${this.mode === "cloud" ? "_cloud" : ""}`
)}
</span>
<ha-password-field
<ha-input
.disabled=${this._loading}
@input=${this._encryptionKeyChanged}
.label=${this.localize(
@@ -178,13 +178,11 @@ class OnboardingRestoreBackupRestore extends LitElement {
)}
.value=${this._encryptionKey}
@keydown=${this._keyDown}
.errorMessage=${this._encryptionKeyWrong
? this.localize(
"ui.panel.page-onboarding.restore.details.restore.encryption.incorrect_key"
)
: ""}
.validationMessage=${this.localize(
"ui.panel.page-onboarding.restore.details.restore.encryption.incorrect_key"
)}
.invalid=${this._encryptionKeyWrong}
></ha-password-field>
></ha-input>
</div>`
: nothing}
@@ -353,7 +351,7 @@ class OnboardingRestoreBackupRestore extends LitElement {
.encryption {
margin-bottom: 32px;
}
.encryption ha-password-field {
.encryption ha-input {
margin-top: 24px;
}
.actions {

View File

@@ -5,15 +5,14 @@ import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-dialog";
import "../../../components/ha-dialog-footer";
import "../../../components/ha-fade-in";
import "../../../components/ha-generic-picker";
import "../../../components/ha-input";
import "../../../components/ha-markdown";
import "../../../components/ha-password-field";
import type { PickerComboBoxItem } from "../../../components/ha-picker-combo-box";
import "../../../components/ha-spinner";
import "../../../components/ha-textfield";
import "../../../components/ha-dialog";
import type {
ApplicationCredential,
ApplicationCredentialsConfig,
@@ -69,6 +68,7 @@ export class DialogAddApplicationCredential extends LitElement {
this._params = params;
this._domain = params.selectedDomain;
this._manifest = params.manifest;
this._invalid = false;
this._name = "";
this._description = "";
this._clientId = "";
@@ -195,7 +195,7 @@ export class DialogAddApplicationCredential extends LitElement {
.content=${this._description}
></ha-markdown>`
: nothing}
<ha-textfield
<ha-input
class="name"
name="name"
.label=${this.hass.localize(
@@ -205,12 +205,12 @@ export class DialogAddApplicationCredential extends LitElement {
.invalid=${this._invalid && !this._name}
required
@input=${this._handleValueChanged}
.errorMessage=${this.hass.localize(
.validationMessage=${this.hass.localize(
"ui.common.error_required"
)}
dialogInitialFocus
></ha-textfield>
<ha-textfield
></ha-input>
<ha-input
class="clientId"
name="clientId"
.label=${this.hass.localize(
@@ -220,16 +220,17 @@ export class DialogAddApplicationCredential extends LitElement {
.invalid=${this._invalid && !this._clientId}
required
@input=${this._handleValueChanged}
.errorMessage=${this.hass.localize(
.validationMessage=${this.hass.localize(
"ui.common.error_required"
)}
dialogInitialFocus
.helper=${this.hass.localize(
.hint=${this.hass.localize(
"ui.panel.config.application_credentials.editor.client_id_helper"
)}
helperPersistent
></ha-textfield>
<ha-password-field
></ha-input>
<ha-input
type="password"
password-toggle
.label=${this.hass.localize(
"ui.panel.config.application_credentials.editor.client_secret"
)}
@@ -238,14 +239,13 @@ export class DialogAddApplicationCredential extends LitElement {
.invalid=${this._invalid && !this._clientSecret}
required
@input=${this._handleValueChanged}
.errorMessage=${this.hass.localize(
.validationMessage=${this.hass.localize(
"ui.common.error_required"
)}
.helper=${this.hass.localize(
.hint=${this.hass.localize(
"ui.panel.config.application_credentials.editor.client_secret_helper"
)}
helperPersistent
></ha-password-field>
></ha-input>
</div>
<ha-dialog-footer slot="footer">
@@ -377,11 +377,6 @@ export class DialogAddApplicationCredential extends LitElement {
display: flex;
padding: var(--ha-space-2) 0;
}
ha-textfield {
display: block;
margin-top: var(--ha-space-4);
margin-bottom: var(--ha-space-4);
}
a {
text-decoration: none;
}

View File

@@ -1,10 +1,10 @@
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-input";
import type { HaInput } from "../../../../../components/ha-input";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-md-textfield";
import type { HaMdTextfield } from "../../../../../components/ha-md-textfield";
import "../../../../../components/ha-select";
import type { SupervisorUpdateConfig } from "../../../../../data/supervisor/update";
import type { HomeAssistant, ValueChangedEvent } from "../../../../../types";
@@ -62,7 +62,7 @@ class HaBackupConfigAddon extends LitElement {
`ui.panel.config.backup.settings.app_update_backup.retention_description`
)}
</span>
<ha-md-textfield
<ha-input
slot="end"
@change=${this._backupRetentionChanged}
.value=${this.supervisorUpdateConfig?.add_on_backup_retain_copies?.toString() ||
@@ -70,11 +70,13 @@ class HaBackupConfigAddon extends LitElement {
type="number"
min=${MIN_RETENTION_VALUE.toString()}
step="1"
.suffixText=${this.hass.localize(
"ui.panel.config.backup.schedule.retention_units.copies"
)}
>
</ha-md-textfield>
<span slot="end">
${this.hass.localize(
"ui.panel.config.backup.schedule.retention_units.copies"
)}
</span>
</ha-input>
</ha-md-list-item>
</ha-md-list>
`;
@@ -92,7 +94,7 @@ class HaBackupConfigAddon extends LitElement {
}
private _backupRetentionChanged(ev) {
const target = ev.currentTarget as HaMdTextfield;
const target = ev.currentTarget as HaInput;
const add_on_backup_retain_copies = Number(target.value);
if (add_on_backup_retain_copies >= MIN_RETENTION_VALUE) {
fireEvent(this, "update-config-changed", {
@@ -115,7 +117,7 @@ class HaBackupConfigAddon extends LitElement {
ha-select {
min-width: 210px;
}
ha-md-textfield {
ha-input {
width: 210px;
}
@media all and (max-width: 450px) {
@@ -123,7 +125,7 @@ class HaBackupConfigAddon extends LitElement {
min-width: 160px;
width: 160px;
}
ha-md-textfield {
ha-input {
width: 160px;
}
}

View File

@@ -3,9 +3,9 @@ import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { clamp } from "../../../../../common/number/clamp";
import "../../../../../components/ha-expansion-panel";
import "../../../../../components/ha-input";
import type { HaInput } from "../../../../../components/ha-input";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-md-textfield";
import type { HaMdTextfield } from "../../../../../components/ha-md-textfield";
import "../../../../../components/ha-select";
import type { HaSelect } from "../../../../../components/ha-select";
import type { BackupConfig, Retention } from "../../../../../data/backup";
@@ -54,7 +54,7 @@ class HaBackupConfigRetention extends LitElement {
@state() private _value = 3;
@query("#value") private _customValueField?: HaMdTextfield;
@query("#value") private _customValueField?: HaInput;
@query("#type") private _customTypeField?: HaSelect;
@@ -141,7 +141,7 @@ class HaBackupConfigRetention extends LitElement {
"ui.panel.config.backup.schedule.custom_retention_label"
)}
</span>
<ha-md-textfield
<ha-input
slot="end"
@change=${this._retentionValueChanged}
.value=${this._value.toString()}
@@ -151,7 +151,7 @@ class HaBackupConfigRetention extends LitElement {
.max=${MAX_VALUE.toString()}
step="1"
>
</ha-md-textfield>
</ha-input>
<ha-select
slot="end"
@selected=${this._retentionTypeChanged}
@@ -208,8 +208,8 @@ class HaBackupConfigRetention extends LitElement {
private _retentionValueChanged(ev) {
ev.stopPropagation();
const target = ev.currentTarget as HaMdTextfield;
const value = parseInt(target.value);
const target = ev.currentTarget as HaInput;
const value = parseInt(target.value ?? "");
const clamped = clamp(value, MIN_VALUE, MAX_VALUE);
target.value = clamped.toString();
@@ -258,14 +258,14 @@ class HaBackupConfigRetention extends LitElement {
width: 160px;
}
}
ha-md-textfield#value {
ha-input#value {
min-width: 70px;
}
ha-select#type {
min-width: 100px;
}
@media all and (max-width: 450px) {
ha-md-textfield#value {
ha-input#value {
min-width: 60px;
margin: 0 -8px;
}

View File

@@ -9,7 +9,6 @@ import "../../../../../components/ha-expansion-panel";
import "../../../../../components/ha-formfield";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-md-textfield";
import "../../../../../components/ha-select";
import "../../../../../components/ha-time-input";
import "../../../../../components/ha-tip";

View File

@@ -13,7 +13,6 @@ import "../../../../components/ha-icon-next";
import "../../../../components/ha-dialog";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-password-field";
import "../../../../components/ha-svg-icon";
import type {
BackupConfig,

View File

@@ -11,7 +11,6 @@ import "../../../../components/ha-icon-button-prev";
import "../../../../components/ha-dialog";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-password-field";
import {
downloadEmergencyKit,
generateEncryptionKey,

View File

@@ -4,9 +4,9 @@ import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-alert";
import "../../../../components/ha-button";
import "../../../../components/ha-dialog-footer";
import "../../../../components/ha-dialog";
import "../../../../components/ha-password-field";
import "../../../../components/ha-dialog-footer";
import "../../../../components/ha-input";
import {
canDecryptBackupOnDownload,
getPreferredAgentForDownload,
@@ -85,12 +85,14 @@ class DialogDownloadDecryptedBackup extends LitElement implements HassDialog {
)}
</p>
<ha-password-field
<ha-input
type="password"
password-toggle
.label=${this.hass.localize(
"ui.panel.config.backup.dialogs.download.encryption_key"
)}
@input=${this._keyChanged}
></ha-password-field>
></ha-input>
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`

View File

@@ -5,13 +5,13 @@ import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-button";
import "../../../../components/ha-dialog-footer";
import "../../../../components/ha-input";
import "../../../../components/ha-spinner";
import "../../../../components/ha-password-field";
import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
import "../../../../components/ha-alert";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-dialog";
import "../../../../components/ha-svg-icon";
import type { RestoreBackupParams } from "../../../../data/backup";
import {
fetchBackupConfig,
@@ -23,11 +23,11 @@ import type {
RestoreBackupState,
} from "../../../../data/backup_manager";
import { subscribeBackupEvents } from "../../../../data/backup_manager";
import { waitForIntegrationSetup } from "../../../../data/integration";
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
import { haStyle, haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import type { RestoreBackupDialogParams } from "./show-dialog-restore-backup";
import { waitForIntegrationSetup } from "../../../../data/integration";
interface FormData {
encryption_key_type: "config" | "custom";
@@ -211,14 +211,16 @@ class DialogRestoreBackup extends LitElement implements HassDialog {
return html`
${this._renderEncryptionIntro()}
<ha-password-field
<ha-input
type="password"
password-toggle
autofocus
@input=${this._passwordChanged}
.label=${this.hass.localize(
"ui.panel.config.backup.dialogs.restore.encryption.input_label"
)}
.value=${this._userPassword || ""}
></ha-password-field>
></ha-input>
`;
}
@@ -387,10 +389,6 @@ class DialogRestoreBackup extends LitElement implements HassDialog {
display: block;
margin-top: 16px;
}
ha-password-field {
display: block;
margin-top: 16px;
}
`,
];
}

View File

@@ -5,12 +5,11 @@ import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
import "../../../../components/ha-button";
import "../../../../components/ha-dialog";
import "../../../../components/ha-dialog-footer";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-dialog";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-password-field";
import {
downloadEmergencyKit,
generateEncryptionKey,

View File

@@ -11,7 +11,6 @@ import "../../../../components/ha-icon-button-prev";
import "../../../../components/ha-dialog";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-password-field";
import { downloadEmergencyKit } from "../../../../data/backup";
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
import { haStyle, haStyleDialog } from "../../../../resources/styles";

View File

@@ -13,7 +13,6 @@ import "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-next";
import "../../../components/ha-password-field";
import "../../../components/ha-svg-icon";
import type { BackupAgent, BackupConfig } from "../../../data/backup";
import { updateBackupConfig } from "../../../data/backup";

View File

@@ -2,26 +2,24 @@ import type { TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/buttons/ha-progress-button";
import "../../../../components/ha-alert";
import "../../../../components/ha-card";
import "../../../../components/ha-button";
import "../../../../components/ha-password-field";
import type { HaPasswordField } from "../../../../components/ha-password-field";
import "../../../../components/ha-textfield";
import type { HaTextField } from "../../../../components/ha-textfield";
import { haStyle } from "../../../../resources/styles";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/ha-card";
import "../../../../components/ha-input";
import type { HaInput } from "../../../../components/ha-input";
import { setAssistPipelinePreferred } from "../../../../data/assist_pipeline";
import { cloudLogin } from "../../../../data/cloud";
import { loginHaCloud } from "../../../../data/onboarding";
import { haStyle } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import {
showAlertDialog,
showConfirmationDialog,
showPromptDialog,
} from "../../../lovelace/custom-card-helpers";
import { setAssistPipelinePreferred } from "../../../../data/assist_pipeline";
import { showCloudAlreadyConnectedDialog } from "../dialog-cloud-already-connected/show-dialog-cloud-already-connected";
import type { HomeAssistant } from "../../../../types";
import { loginHaCloud } from "../../../../data/onboarding";
@customElement("cloud-login")
export class CloudLogin extends LitElement {
@@ -40,9 +38,9 @@ export class CloudLogin extends LitElement {
@property({ type: Boolean, attribute: "card-less" }) public cardLess = false;
@query("#email", true) public emailField!: HaTextField;
@query("#email", true) public emailField!: HaInput;
@query("#password", true) private _passwordField!: HaPasswordField;
@query("#password", true) private _passwordField!: HaInput;
@state() private _error?: string;
@@ -71,13 +69,14 @@ export class CloudLogin extends LitElement {
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: nothing}
<ha-textfield
<ha-input
.label=${this.localize(
`ui.panel.${this.translationKeyPanel}.login.email`
)}
id="email"
name="username"
type="email"
hint="This should be a real email address, not an alias. If you used an alias to register, use the email address that the alias forwards to."
autocomplete="username"
required
.value=${this.email ?? ""}
@@ -86,10 +85,13 @@ export class CloudLogin extends LitElement {
.validationMessage=${this.localize(
`ui.panel.${this.translationKeyPanel}.login.email_error_msg`
)}
></ha-textfield>
<ha-password-field
></ha-input>
<ha-input
id="password"
type="password"
password-toggle
name="password"
hint="Use your nabu casa password, not your Home Assistant password. If you don't remember it, use the forgot password link below."
.label=${this.localize(
`ui.panel.${this.translationKeyPanel}.login.password`
)}
@@ -101,7 +103,7 @@ export class CloudLogin extends LitElement {
.validationMessage=${this.localize(
`ui.panel.${this.translationKeyPanel}.login.password_error_msg`
)}
></ha-password-field>
></ha-input>
</div>
<div class="card-actions">
<ha-button
@@ -277,21 +279,29 @@ export class CloudLogin extends LitElement {
private async _handleLogin() {
if (!this._inProgress) {
let valid = true;
if (!this.emailField.reportValidity()) {
this.emailField.focus();
return;
valid = false;
}
if (!this._passwordField.reportValidity()) {
this._passwordField.focus();
if (valid) {
this._passwordField.focus();
}
valid = false;
}
if (!valid) {
return;
}
this._inProgress = true;
this._login(
this.emailField.value,
this._passwordField.value,
this.emailField.value as string,
this._passwordField.value as string,
this.checkConnection
);
}

View File

@@ -5,14 +5,13 @@ import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/buttons/ha-progress-button";
import "../../../../components/ha-alert";
import "../../../../components/ha-card";
import "../../../../components/ha-textfield";
import type { HaTextField } from "../../../../components/ha-textfield";
import "../../../../components/ha-input";
import type { HaInput } from "../../../../components/ha-input";
import { cloudRegister, cloudResendVerification } from "../../../../data/cloud";
import "../../../../layouts/hass-subpage";
import { haStyle } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import "../../ha-config-section";
import "../../../../components/ha-password-field";
@customElement("cloud-register")
export class CloudRegister extends LitElement {
@@ -30,9 +29,9 @@ export class CloudRegister extends LitElement {
@state() private _error?: string;
@query("#email", true) private _emailField!: HaTextField;
@query("#email", true) private _emailField!: HaInput;
@query("#password", true) private _passwordField!: HaTextField;
@query("#password", true) private _passwordField!: HaInput;
protected render(): TemplateResult {
return html`
@@ -131,7 +130,7 @@ export class CloudRegister extends LitElement {
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: ""}
<ha-textfield
<ha-input
autofocus
id="email"
name="email"
@@ -143,12 +142,14 @@ export class CloudRegister extends LitElement {
required
.value=${this.email ?? ""}
@keydown=${this._keyDown}
validationMessage=${this.hass.localize(
.validationMessage=${this.hass.localize(
"ui.panel.config.cloud.register.email_error_msg"
)}
></ha-textfield>
<ha-password-field
></ha-input>
<ha-input
id="password"
type="password"
password-toggle
name="password"
.label=${this.hass.localize(
"ui.panel.config.cloud.register.password"
@@ -158,10 +159,10 @@ export class CloudRegister extends LitElement {
minlength="8"
required
@keydown=${this._keyDown}
validationMessage=${this.hass.localize(
.validationMessage=${this.hass.localize(
"ui.panel.config.cloud.register.password_error_msg"
)}
></ha-password-field>
></ha-input>
</div>
<div class="card-actions">
<button
@@ -209,8 +210,8 @@ export class CloudRegister extends LitElement {
return;
}
const email = emailField.value.toLowerCase();
const password = passwordField.value;
const email = emailField.value?.toLowerCase() || "";
const password = passwordField.value || "";
this._requestInProgress = true;
@@ -235,7 +236,7 @@ export class CloudRegister extends LitElement {
return;
}
const email = emailField.value;
const email = emailField.value || "";
const doResend = async (username: string) => {
try {

View File

@@ -36,7 +36,7 @@ import { showQuickBar } from "../../../dialogs/quick-bar/show-dialog-quick-bar";
import { showRestartDialog } from "../../../dialogs/restart/show-dialog-restart";
import { showShortcutsDialog } from "../../../dialogs/shortcuts/show-shortcuts-dialog";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { haStyle } from "../../../resources/styles";
import { haStyle, haStyleScrollbar } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import { isMac } from "../../../util/is_mac";
@@ -255,88 +255,90 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
</ha-dropdown-item>
</ha-dropdown>
<ha-config-section
.narrow=${this.narrow}
.isWide=${this.isWide}
full-width
>
${repairsIssues.length || canInstallUpdates.length
? html`<ha-card outlined>
${repairsIssues.length
? html`
<ha-config-repairs
<div class="content ha-scrollbar">
<ha-config-section
.narrow=${this.narrow}
.isWide=${this.isWide}
full-width
>
${repairsIssues.length || canInstallUpdates.length
? html`<ha-card outlined>
${repairsIssues.length
? html`
<ha-config-repairs
.hass=${this.hass}
.narrow=${this.narrow}
.total=${totalRepairIssues}
.repairsIssues=${repairsIssues}
></ha-config-repairs>
${totalRepairIssues > repairsIssues.length
? html`
<ha-assist-chip
href="/config/repairs"
.label=${this.hass.localize(
"ui.panel.config.repairs.more_repairs",
{
count:
totalRepairIssues - repairsIssues.length,
}
)}
>
</ha-assist-chip>
`
: ""}
`
: ""}
${repairsIssues.length && canInstallUpdates.length
? html`<hr />`
: ""}
${canInstallUpdates.length
? html`
<ha-config-updates
.hass=${this.hass}
.narrow=${this.narrow}
.total=${totalUpdates}
.updateEntities=${canInstallUpdates}
.isInstallable=${true}
></ha-config-updates>
${totalUpdates > canInstallUpdates.length
? html`
<ha-assist-chip
href="/config/updates"
label=${this.hass.localize(
"ui.panel.config.updates.more_updates",
{
count:
totalUpdates - canInstallUpdates.length,
}
)}
>
</ha-assist-chip>
`
: ""}
`
: ""}
</ha-card>`
: ""}
${this._pages(
this.cloudStatus,
isComponentLoaded(this.hass, "cloud"),
this.hass.auth.external?.config.hasSettingsScreen
).map((categoryPages) =>
categoryPages.length === 0
? nothing
: html`
<ha-card outlined>
<ha-config-navigation
.hass=${this.hass}
.narrow=${this.narrow}
.total=${totalRepairIssues}
.repairsIssues=${repairsIssues}
></ha-config-repairs>
${totalRepairIssues > repairsIssues.length
? html`
<ha-assist-chip
href="/config/repairs"
.label=${this.hass.localize(
"ui.panel.config.repairs.more_repairs",
{
count:
totalRepairIssues - repairsIssues.length,
}
)}
>
</ha-assist-chip>
`
: ""}
`
: ""}
${repairsIssues.length && canInstallUpdates.length
? html`<hr />`
: ""}
${canInstallUpdates.length
? html`
<ha-config-updates
.hass=${this.hass}
.narrow=${this.narrow}
.total=${totalUpdates}
.updateEntities=${canInstallUpdates}
.isInstallable=${true}
></ha-config-updates>
${totalUpdates > canInstallUpdates.length
? html`
<ha-assist-chip
href="/config/updates"
label=${this.hass.localize(
"ui.panel.config.updates.more_updates",
{
count:
totalUpdates - canInstallUpdates.length,
}
)}
>
</ha-assist-chip>
`
: ""}
`
: ""}
</ha-card>`
: ""}
${this._pages(
this.cloudStatus,
isComponentLoaded(this.hass, "cloud"),
this.hass.auth.external?.config.hasSettingsScreen
).map((categoryPages) =>
categoryPages.length === 0
? nothing
: html`
<ha-card outlined>
<ha-config-navigation
.hass=${this.hass}
.narrow=${this.narrow}
.pages=${categoryPages}
></ha-config-navigation>
</ha-card>
`
)}
<ha-tip .hass=${this.hass}>${this._tip}</ha-tip>
</ha-config-section>
.pages=${categoryPages}
></ha-config-navigation>
</ha-card>
`
)}
<ha-tip .hass=${this.hass}>${this._tip}</ha-tip>
</ha-config-section>
</div>
</ha-top-app-bar-fixed>
`;
}
@@ -392,7 +394,36 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleScrollbar,
css`
:host {
display: block;
height: 100%;
}
ha-top-app-bar-fixed {
height: 100%;
overflow: hidden;
}
.content {
height: calc(
100vh - var(--header-height, 0px) - var(
--safe-area-inset-top,
0px
) - var(--safe-area-inset-bottom, 0px)
);
height: calc(
100dvh - var(--header-height, 0px) - var(
--safe-area-inset-top,
0px
) - var(--safe-area-inset-bottom, 0px)
);
padding-bottom: var(--ha-space-5);
box-sizing: border-box;
overflow-x: hidden;
}
:host(:not([narrow])) ha-card:last-child {
margin-bottom: 24px;
}

View File

@@ -8,7 +8,7 @@ import "../../../../components/ha-duration-input";
import type { HaDurationData } from "../../../../components/ha-duration-input";
import "../../../../components/ha-formfield";
import "../../../../components/ha-icon-picker";
import "../../../../components/ha-textfield";
import "../../../../components/ha-input";
import type { ForDict } from "../../../../data/automation";
import type { DurationDict, Timer } from "../../../../data/timer";
import { haStyle } from "../../../../resources/styles";
@@ -66,21 +66,21 @@ class HaTimerForm extends LitElement {
return html`
<div class="form">
<ha-textfield
<ha-input
.value=${this._name}
.configValue=${"name"}
@input=${this._valueChanged}
.label=${this.hass!.localize(
"ui.dialogs.helper_settings.generic.name"
)}
autoValidate
auto-validate
required
.validationMessage=${this.hass!.localize(
"ui.dialogs.helper_settings.required_error_msg"
)}
dialogInitialFocus
.disabled=${this.disabled}
></ha-textfield>
></ha-input>
<ha-icon-picker
.hass=${this.hass}
.value=${this._icon}

View File

@@ -11,16 +11,15 @@ import "../../../components/ha-dropdown-item";
import "../../../components/ha-expansion-panel";
import "../../../components/ha-formfield";
import "../../../components/ha-icon-button";
import "../../../components/ha-input";
import type { HaInput } from "../../../components/ha-input";
import "../../../components/ha-list";
import "../../../components/ha-list-item";
import "../../../components/ha-password-field";
import "../../../components/ha-radio";
import type { HaRadio } from "../../../components/ha-radio";
import "../../../components/ha-spinner";
import "../../../components/ha-tab-group";
import "../../../components/ha-tab-group-tab";
import "../../../components/ha-textfield";
import type { HaTextField } from "../../../components/ha-textfield";
import { extractApiErrorMessage } from "../../../data/hassio/common";
import {
type AccessPoint,
@@ -233,7 +232,9 @@ export class HassioNetwork extends LitElement {
${this._wifiConfiguration.auth === "wpa-psk" ||
this._wifiConfiguration.auth === "wep"
? html`
<ha-password-field
<ha-input
type="password"
password-toggle
id="psk"
.label=${this.hass.localize(
"ui.panel.config.network.supervisor.wifi_password"
@@ -241,7 +242,7 @@ export class HassioNetwork extends LitElement {
.version=${"wifi"}
@change=${this._handleInputValueChangedWifi}
>
</ha-password-field>
</ha-input>
`
: nothing}
`
@@ -388,7 +389,7 @@ export class HassioNetwork extends LitElement {
const { ip, mask, prefix } = parseAddress(address);
return html`
<div class="address-row">
<ha-textfield
<ha-input
id="address"
.label=${this.hass.localize(
"ui.panel.config.network.supervisor.ip"
@@ -399,10 +400,10 @@ export class HassioNetwork extends LitElement {
@change=${this._handleInputValueChanged}
.disabled=${disableInputs}
>
</ha-textfield>
</ha-input>
${version === "ipv6"
? html`
<ha-textfield
<ha-input
id="prefix"
.label=${this.hass.localize(
"ui.panel.config.network.supervisor.prefix"
@@ -413,10 +414,10 @@ export class HassioNetwork extends LitElement {
@change=${this._handleInputValueChanged}
.disabled=${disableInputs}
>
</ha-textfield>
</ha-input>
`
: html`
<ha-textfield
<ha-input
id="netmask"
.label=${this.hass.localize(
"ui.panel.config.network.supervisor.netmask"
@@ -427,7 +428,7 @@ export class HassioNetwork extends LitElement {
@change=${this._handleInputValueChanged}
.disabled=${disableInputs}
>
</ha-textfield>
</ha-input>
`}
${this._interface![version].address.length > 1 &&
!disableInputs
@@ -461,7 +462,7 @@ export class HassioNetwork extends LitElement {
</ha-button>
`
: nothing}
<ha-textfield
<ha-input
id="gateway"
.label=${this.hass.localize(
"ui.panel.config.network.supervisor.gateway"
@@ -471,12 +472,12 @@ export class HassioNetwork extends LitElement {
@change=${this._handleInputValueChanged}
.disabled=${disableInputs}
>
</ha-textfield>
</ha-input>
<div class="nameservers">
${nameservers.map(
(nameserver: string, index: number) => html`
<div class="address-row">
<ha-textfield
<ha-input
id="nameserver"
.label=${`${this.hass.localize(
"ui.panel.config.network.supervisor.dns_server"
@@ -486,10 +487,11 @@ export class HassioNetwork extends LitElement {
.index=${index}
@change=${this._handleInputValueChanged}
>
</ha-textfield>
</ha-input>
${this._interface![version].nameservers?.length > 1
? html`
<ha-icon-button
slot="end"
.label=${this.hass.localize("ui.common.delete")}
.path=${mdiDeleteOutline}
.version=${version}
@@ -677,12 +679,13 @@ export class HassioNetwork extends LitElement {
}
private _handleInputValueChanged(ev: Event): void {
const source = ev.target as HaTextField;
const source = ev.target as HaInput;
const value = source.value;
const version = (ev.target as any).version as "ipv4" | "ipv6";
const id = source.id;
if (!value || !this._interface?.[version]) {
source.reportValidity();
return;
}
@@ -718,7 +721,7 @@ export class HassioNetwork extends LitElement {
}
private _handleInputValueChangedWifi(ev: Event): void {
const source = ev.target as HaTextField;
const source = ev.target as HaInput;
const value = source.value;
const id = source.id;
@@ -727,6 +730,7 @@ export class HassioNetwork extends LitElement {
!this._wifiConfiguration ||
this._wifiConfiguration![id] === value
) {
source.reportValidity();
return;
}
this._dirty = true;
@@ -819,26 +823,25 @@ export class HassioNetwork extends LitElement {
--expansion-panel-summary-padding: 0 16px;
margin: 4px 0;
}
ha-textfield {
display: block;
margin-top: 16px;
}
.address-row {
display: flex;
flex-direction: row;
gap: var(--ha-space-2);
align-items: center;
}
.address-row ha-textfield {
.address-row ha-input {
flex: 1;
}
.address-row #prefix {
flex: none;
width: 95px;
}
ha-icon-button {
color: var(--secondary-text-color);
}
.address-row ha-icon-button {
--ha-icon-button-size: 36px;
margin-top: 16px;
margin-top: var(--ha-space-5);
}
ha-dropdown {
display: block;

View File

@@ -4,14 +4,14 @@ import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-dialog";
import "../../../components/ha-dialog-footer";
import "../../../components/ha-icon-button";
import "../../../components/ha-input";
import type { HaInput } from "../../../components/ha-input";
import "../../../components/ha-md-list-item";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/ha-textfield";
import type { HaTextField } from "../../../components/ha-textfield";
import "../../../components/ha-dialog";
import { createAuthForUser } from "../../../data/auth";
import type { User } from "../../../data/user";
import {
@@ -23,7 +23,6 @@ import {
import { haStyleDialog } from "../../../resources/styles";
import type { HomeAssistant, ValueChangedEvent } from "../../../types";
import type { AddUserDialogParams } from "./show-dialog-add-user";
import "../../../components/ha-password-field";
@customElement("dialog-add-user")
export class DialogAddUser extends LitElement {
@@ -100,7 +99,7 @@ export class DialogAddUser extends LitElement {
<div>
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
${this._allowChangeName
? html`<ha-textfield
? html`<ha-input
class="name"
name="name"
.label=${this.hass.localize(
@@ -114,9 +113,9 @@ export class DialogAddUser extends LitElement {
@input=${this._handleValueChanged}
@blur=${this._maybePopulateUsername}
autofocus
></ha-textfield>`
></ha-input>`
: ""}
<ha-textfield
<ha-input
class="username"
name="username"
.label=${this.hass.localize(
@@ -127,9 +126,11 @@ export class DialogAddUser extends LitElement {
@input=${this._handleValueChanged}
.validationMessage=${this.hass.localize("ui.common.error_required")}
?autofocus=${!this._allowChangeName}
></ha-textfield>
></ha-input>
<ha-password-field
<ha-input
type="password"
password-toggle
.label=${this.hass.localize(
"ui.panel.config.users.add_user.password"
)}
@@ -138,9 +139,11 @@ export class DialogAddUser extends LitElement {
required
@input=${this._handleValueChanged}
.validationMessage=${this.hass.localize("ui.common.error_required")}
></ha-password-field>
></ha-input>
<ha-password-field
<ha-input
type="password"
password-toggle
.label=${this.hass.localize(
"ui.panel.config.users.add_user.password_confirm"
)}
@@ -154,7 +157,7 @@ export class DialogAddUser extends LitElement {
.errorMessage=${this.hass.localize(
"ui.panel.config.users.add_user.password_not_match"
)}
></ha-password-field>
></ha-input>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
@@ -245,7 +248,7 @@ export class DialogAddUser extends LitElement {
private _handleValueChanged(ev: ValueChangedEvent<string>): void {
this._error = undefined;
const target = ev.target as HaTextField;
const target = ev.target as HaInput;
this[`_${target.name}`] = target.value;
}
@@ -318,11 +321,6 @@ export class DialogAddUser extends LitElement {
display: flex;
padding: 8px 0;
}
ha-textfield,
ha-password-field {
display: block;
margin-bottom: 8px;
}
ha-md-list-item {
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;

View File

@@ -7,7 +7,11 @@ import type { HomeAssistant } from "../../../types";
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
import { shouldShowFloorsAndAreas } from "./show-floors-and-areas";
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
import type { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
import {
LARGE_SCREEN_CONDITION,
SMALL_SCREEN_CONDITION,
} from "../../lovelace/strategies/helpers/screen-conditions";
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
@customElement("power-view-strategy")
export class PowerViewStrategy extends ReactiveElement {
@@ -45,15 +49,22 @@ export class PowerViewStrategy extends ReactiveElement {
(source) => source.type === "gas" && source.stat_rate
);
const tileSection: LovelaceSectionConfig = {
type: "grid",
cards: [],
column_span: 2,
};
const chartsSection: LovelaceSectionConfig = {
type: "grid",
cards: [],
column_span: 2,
};
const badges: LovelaceBadgeConfig[] = [];
const tiles: LovelaceCardConfig[] = [];
const view: LovelaceViewConfig = {
type: "sections",
sections: [chartsSection],
sections: [tileSection, chartsSection],
max_columns: 2,
};
// No sources configured
@@ -69,10 +80,11 @@ export class PowerViewStrategy extends ReactiveElement {
}
if (hasPowerSources) {
badges.push({
const card = {
type: "power-total",
collection_key: collectionKey,
});
};
tiles.push(card);
chartsSection.cards!.push({
title: hass.localize("ui.panel.energy.cards.power_sources_graph_title"),
@@ -85,17 +97,19 @@ export class PowerViewStrategy extends ReactiveElement {
}
if (hasGasSources) {
badges.push({
const card = {
type: "gas-total",
collection_key: collectionKey,
});
};
tiles.push({ ...card });
}
if (hasWaterSources) {
badges.push({
const card = {
type: "water-total",
collection_key: collectionKey,
});
};
tiles.push({ ...card });
}
if (hasPowerDevices) {
@@ -134,8 +148,21 @@ export class PowerViewStrategy extends ReactiveElement {
});
}
if (badges.length) {
view.badges = badges;
tiles.forEach((card) => {
tileSection.cards!.push({
...card,
grid_options: { columns: 24 / tiles.length },
});
});
if (tiles.length > 2) {
// On small screens with 3 tiles, show them in 1 column
tileSection.visibility = [LARGE_SCREEN_CONDITION];
view.sections!.unshift({
type: "grid",
cards: tiles,
visibility: [SMALL_SCREEN_CONDITION],
});
}
return view;

View File

@@ -48,20 +48,3 @@ export interface EntityBadgeConfig extends LovelaceBadgeConfig {
*/
display_type?: DisplayType;
}
interface EnergyTotalBadgeConfig extends LovelaceBadgeConfig {
title?: string;
collection_key?: string;
}
export interface PowerTotalBadgeConfig extends EnergyTotalBadgeConfig {
type: "power-total";
}
export interface WaterTotalBadgeConfig extends EnergyTotalBadgeConfig {
type: "water-total";
}
export interface GasTotalBadgeConfig extends EnergyTotalBadgeConfig {
type: "gas-total";
}

View File

@@ -3,8 +3,11 @@ import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../components/ha-badge";
import "../../../../components/ha-card";
import "../../../../components/ha-svg-icon";
import "../../../../components/tile/ha-tile-container";
import "../../../../components/tile/ha-tile-icon";
import "../../../../components/tile/ha-tile-info";
import type { EnergyData, EnergyPreferences } from "../../../../data/energy";
import {
formatFlowRateShort,
@@ -13,17 +16,18 @@ import {
} from "../../../../data/energy";
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../../../types";
import type { LovelaceBadge } from "../../types";
import type { GasTotalBadgeConfig } from "../types";
import type { LovelaceCard, LovelaceGridOptions } from "../../types";
import { tileCardStyle } from "../tile/tile-card-style";
import type { GasTotalCardConfig } from "../types";
@customElement("hui-gas-total-badge")
export class HuiGasTotalBadge
@customElement("hui-gas-total-card")
export class HuiGasTotalCard
extends SubscribeMixin(LitElement)
implements LovelaceBadge
implements LovelaceCard
{
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _config?: GasTotalBadgeConfig;
@state() private _config?: GasTotalCardConfig;
@state() private _data?: EnergyData;
@@ -31,7 +35,7 @@ export class HuiGasTotalBadge
protected hassSubscribeRequiredHostProps = ["_config"];
public setConfig(config: GasTotalBadgeConfig): void {
public setConfig(config: GasTotalCardConfig): void {
this._config = config;
}
@@ -45,19 +49,34 @@ export class HuiGasTotalBadge
];
}
public getCardSize(): Promise<number> | number {
return 1;
}
getGridOptions(): LovelaceGridOptions {
return {
columns: 12,
min_columns: 6,
rows: 1,
min_rows: 1,
};
}
protected shouldUpdate(changedProps: PropertyValues): boolean {
if (changedProps.has("_config") || changedProps.has("_data")) {
return true;
}
// Check if any of the tracked entity states have changed
if (changedProps.has("hass")) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || !this._entities.size) {
return true;
}
// Only update if one of our tracked entities changed
for (const entityId of this._entities) {
if (oldHass.states[entityId] !== this.hass?.states[entityId]) {
if (oldHass.states[entityId] !== this.hass.states[entityId]) {
return true;
}
}
@@ -103,22 +122,32 @@ export class HuiGasTotalBadge
this.hass.localize("ui.panel.lovelace.cards.energy.gas_total_title");
return html`
<ha-badge .label=${name}>
<ha-svg-icon slot="icon" .path=${mdiFire}></ha-svg-icon>
${displayValue}
</ha-badge>
<ha-card>
<ha-tile-container .interactive=${false}>
<ha-tile-icon slot="icon" data-domain="sensor" data-state="active">
<ha-svg-icon slot="icon" .path=${mdiFire}></ha-svg-icon>
</ha-tile-icon>
<ha-tile-info slot="info">
<span slot="primary" class="primary">${name}</span>
<span slot="secondary" class="secondary">${displayValue}</span>
</ha-tile-info>
</ha-tile-container>
</ha-card>
`;
}
static styles = css`
ha-badge {
--badge-color: var(--energy-gas-color);
}
`;
static styles = [
tileCardStyle,
css`
:host {
--tile-color: var(--energy-gas-color);
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"hui-gas-total-badge": HuiGasTotalBadge;
"hui-gas-total-card": HuiGasTotalCard;
}
}

View File

@@ -4,8 +4,11 @@ import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { formatNumber } from "../../../../common/number/format_number";
import "../../../../components/ha-badge";
import "../../../../components/ha-card";
import "../../../../components/ha-svg-icon";
import "../../../../components/tile/ha-tile-container";
import "../../../../components/tile/ha-tile-icon";
import "../../../../components/tile/ha-tile-info";
import type { EnergyData, EnergyPreferences } from "../../../../data/energy";
import {
getEnergyDataCollection,
@@ -13,17 +16,18 @@ import {
} from "../../../../data/energy";
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../../../types";
import type { LovelaceBadge } from "../../types";
import type { PowerTotalBadgeConfig } from "../types";
import type { LovelaceCard, LovelaceGridOptions } from "../../types";
import { tileCardStyle } from "../tile/tile-card-style";
import type { PowerTotalCardConfig } from "../types";
@customElement("hui-power-total-badge")
export class HuiPowerTotalBadge
@customElement("hui-power-total-card")
export class HuiPowerTotalCard
extends SubscribeMixin(LitElement)
implements LovelaceBadge
implements LovelaceCard
{
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _config?: PowerTotalBadgeConfig;
@state() private _config?: PowerTotalCardConfig;
@state() private _data?: EnergyData;
@@ -31,7 +35,7 @@ export class HuiPowerTotalBadge
protected hassSubscribeRequiredHostProps = ["_config"];
public setConfig(config: PowerTotalBadgeConfig): void {
public setConfig(config: PowerTotalCardConfig): void {
this._config = config;
}
@@ -45,19 +49,34 @@ export class HuiPowerTotalBadge
];
}
public getCardSize(): Promise<number> | number {
return 1;
}
getGridOptions(): LovelaceGridOptions {
return {
columns: 12,
min_columns: 6,
rows: 1,
min_rows: 1,
};
}
protected shouldUpdate(changedProps: PropertyValues): boolean {
if (changedProps.has("_config") || changedProps.has("_data")) {
return true;
}
// Check if any of the tracked entity states have changed
if (changedProps.has("hass")) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || !this._entities.size) {
return true;
}
// Only update if one of our tracked entities changed
for (const entityId of this._entities) {
if (oldHass.states[entityId] !== this.hass?.states[entityId]) {
if (oldHass.states[entityId] !== this.hass.states[entityId]) {
return true;
}
}
@@ -75,10 +94,10 @@ export class HuiPowerTotalBadge
this._entities.clear();
let solar = 0;
let fromGrid = 0;
let toGrid = 0;
let fromBattery = 0;
let toBattery = 0;
let from_grid = 0;
let to_grid = 0;
let from_battery = 0;
let to_battery = 0;
prefs.energy_sources.forEach((source) => {
if (source.type === "solar" && source.stat_rate) {
@@ -86,17 +105,17 @@ export class HuiPowerTotalBadge
if (value > 0) solar += value;
} else if (source.type === "grid" && source.stat_rate) {
const value = this._getCurrentPower(source.stat_rate);
if (value > 0) fromGrid += value;
else if (value < 0) toGrid += Math.abs(value);
if (value > 0) from_grid += value;
else if (value < 0) to_grid += Math.abs(value);
} else if (source.type === "battery" && source.stat_rate) {
const value = this._getCurrentPower(source.stat_rate);
if (value > 0) fromBattery += value;
else if (value < 0) toBattery += Math.abs(value);
if (value > 0) from_battery += value;
else if (value < 0) to_battery += Math.abs(value);
}
});
const usedTotal = fromGrid + solar + fromBattery - toGrid - toBattery;
return Math.max(0, usedTotal);
const used_total = from_grid + solar + from_battery - to_grid - to_battery;
return Math.max(0, used_total);
}
protected render() {
@@ -122,22 +141,35 @@ export class HuiPowerTotalBadge
this.hass.localize("ui.panel.lovelace.cards.energy.power_total_title");
return html`
<ha-badge .label=${name}>
<ha-svg-icon slot="icon" .path=${mdiHomeLightningBolt}></ha-svg-icon>
${displayValue}
</ha-badge>
<ha-card>
<ha-tile-container .interactive=${false}>
<ha-tile-icon slot="icon" data-domain="sensor" data-state="active">
<ha-svg-icon
slot="icon"
.path=${mdiHomeLightningBolt}
></ha-svg-icon>
</ha-tile-icon>
<ha-tile-info slot="info">
<span slot="primary" class="primary">${name}</span>
<span slot="secondary" class="secondary">${displayValue}</span>
</ha-tile-info>
</ha-tile-container>
</ha-card>
`;
}
static styles = css`
ha-badge {
--badge-color: var(--primary-color);
}
`;
static styles = [
tileCardStyle,
css`
:host {
--tile-color: var(--primary-color);
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"hui-power-total-badge": HuiPowerTotalBadge;
"hui-power-total-card": HuiPowerTotalCard;
}
}

View File

@@ -3,8 +3,11 @@ import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../components/ha-badge";
import "../../../../components/ha-card";
import "../../../../components/ha-svg-icon";
import "../../../../components/tile/ha-tile-container";
import "../../../../components/tile/ha-tile-icon";
import "../../../../components/tile/ha-tile-info";
import type { EnergyData, EnergyPreferences } from "../../../../data/energy";
import {
formatFlowRateShort,
@@ -13,17 +16,18 @@ import {
} from "../../../../data/energy";
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../../../types";
import type { LovelaceBadge } from "../../types";
import type { WaterTotalBadgeConfig } from "../types";
import type { LovelaceCard, LovelaceGridOptions } from "../../types";
import { tileCardStyle } from "../tile/tile-card-style";
import type { WaterTotalCardConfig } from "../types";
@customElement("hui-water-total-badge")
export class HuiWaterTotalBadge
@customElement("hui-water-total-card")
export class HuiWaterTotalCard
extends SubscribeMixin(LitElement)
implements LovelaceBadge
implements LovelaceCard
{
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _config?: WaterTotalBadgeConfig;
@state() private _config?: WaterTotalCardConfig;
@state() private _data?: EnergyData;
@@ -31,7 +35,7 @@ export class HuiWaterTotalBadge
protected hassSubscribeRequiredHostProps = ["_config"];
public setConfig(config: WaterTotalBadgeConfig): void {
public setConfig(config: WaterTotalCardConfig): void {
this._config = config;
}
@@ -45,19 +49,34 @@ export class HuiWaterTotalBadge
];
}
public getCardSize(): Promise<number> | number {
return 1;
}
getGridOptions(): LovelaceGridOptions {
return {
columns: 12,
min_columns: 6,
rows: 1,
min_rows: 1,
};
}
protected shouldUpdate(changedProps: PropertyValues): boolean {
if (changedProps.has("_config") || changedProps.has("_data")) {
return true;
}
// Check if any of the tracked entity states have changed
if (changedProps.has("hass")) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || !this._entities.size) {
return true;
}
// Only update if one of our tracked entities changed
for (const entityId of this._entities) {
if (oldHass.states[entityId] !== this.hass?.states[entityId]) {
if (oldHass.states[entityId] !== this.hass.states[entityId]) {
return true;
}
}
@@ -103,22 +122,32 @@ export class HuiWaterTotalBadge
this.hass.localize("ui.panel.lovelace.cards.energy.water_total_title");
return html`
<ha-badge .label=${name}>
<ha-svg-icon slot="icon" .path=${mdiWater}></ha-svg-icon>
${displayValue}
</ha-badge>
<ha-card>
<ha-tile-container .interactive=${false}>
<ha-tile-icon slot="icon" data-domain="sensor" data-state="active">
<ha-svg-icon slot="icon" .path=${mdiWater}></ha-svg-icon>
</ha-tile-icon>
<ha-tile-info slot="info">
<span slot="primary" class="primary">${name}</span>
<span slot="secondary" class="secondary">${displayValue}</span>
</ha-tile-info>
</ha-tile-container>
</ha-card>
`;
}
static styles = css`
ha-badge {
--badge-color: var(--energy-water-color);
}
`;
static styles = [
tileCardStyle,
css`
:host {
--tile-color: var(--energy-water-color);
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"hui-water-total-badge": HuiWaterTotalBadge;
"hui-water-total-card": HuiWaterTotalCard;
}
}

View File

@@ -265,6 +265,21 @@ export interface PowerSourcesGraphCardConfig extends EnergyCardBaseConfig {
show_legend?: boolean;
}
export interface PowerTotalCardConfig extends EnergyCardBaseConfig {
type: "power-total";
title?: string;
}
export interface WaterTotalCardConfig extends EnergyCardBaseConfig {
type: "water-total";
title?: string;
}
export interface GasTotalCardConfig extends EnergyCardBaseConfig {
type: "gas-total";
title?: string;
}
export interface PowerSankeyCardConfig extends EnergyCardBaseConfig {
type: "power-sankey";
title?: string;

View File

@@ -10,9 +10,6 @@ const ALWAYS_LOADED_TYPES = new Set(["error", "entity"]);
const LAZY_LOAD_TYPES = {
"entity-filter": () => import("../badges/hui-entity-filter-badge"),
"state-label": () => import("../badges/hui-state-label-badge"),
"power-total": () => import("../badges/energy/hui-power-total-badge"),
"gas-total": () => import("../badges/energy/hui-gas-total-badge"),
"water-total": () => import("../badges/energy/hui-water-total-badge"),
};
// This will not return an error card but will throw the error

View File

@@ -71,6 +71,9 @@ const LAZY_LOAD_TYPES = {
import("../cards/water/hui-water-flow-sankey-card"),
"power-sources-graph": () =>
import("../cards/energy/hui-power-sources-graph-card"),
"power-total": () => import("../cards/energy/hui-power-total-card"),
"water-total": () => import("../cards/energy/hui-water-total-card"),
"gas-total": () => import("../cards/energy/hui-gas-total-card"),
"power-sankey": () => import("../cards/energy/hui-power-sankey-card"),
"entity-filter": () => import("../cards/hui-entity-filter-card"),
error: () => import("../cards/hui-error-card"),

View File

@@ -239,28 +239,15 @@ export class LovelacePanel extends LitElement {
const newConfig = checkLovelaceConfig(generatedConfig) as LovelaceConfig;
// Ask to regenerate if the config changed
// Regenerate if the config changed
if (!deepEqual(newConfig, oldConfig)) {
this._askRegenerateStrategyConfig();
this._regenerateStrategyConfig();
}
};
private _strategyConfigChanged = (ev: CustomEvent) => {
ev.stopPropagation();
this._askRegenerateStrategyConfig();
};
private _askRegenerateStrategyConfig = () => {
showToast(this, {
message: this.hass!.localize("ui.panel.lovelace.changed_toast.message"),
action: {
action: () => this._regenerateStrategyConfig(),
text: this.hass!.localize("ui.common.refresh"),
},
duration: -1,
id: "regenerate-strategy-config",
dismissable: false,
});
this._regenerateStrategyConfig();
};
private async _regenerateStrategyConfig() {
@@ -313,8 +300,14 @@ export class LovelacePanel extends LitElement {
this._fetchConfigOnConnect = true;
return;
}
if (!this.lovelace?.editMode && this._panelState !== "yaml-editor") {
this._fetchConfig(false);
return;
}
showToast(this, {
message: this.hass!.localize("ui.panel.lovelace.changed_toast.message"),
message: this.hass!.localize(
"ui.panel.lovelace.externally_updated_toast.message"
),
action: {
action: () => this._fetchConfig(false),
text: this.hass!.localize("ui.common.refresh"),

View File

@@ -7,21 +7,20 @@ import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { array, assert, object, optional, string, type } from "superstruct";
import { deepEqual } from "../../common/util/deep-equal";
import "../../components/ha-button";
import "../../components/ha-code-editor";
import type { HaCodeEditor } from "../../components/ha-code-editor";
import "../../components/ha-icon-button";
import "../../components/ha-button";
import "../../components/ha-top-app-bar-fixed";
import type { LovelaceRawConfig } from "../../data/lovelace/config/types";
import { isStrategyDashboard } from "../../data/lovelace/config/types";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../dialogs/generic/show-dialog-box";
import { haStyle } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
import { showToast } from "../../util/toast";
import type { Lovelace } from "./types";
import "../../components/ha-top-app-bar-fixed";
import type { LovelaceRawConfig } from "../../data/lovelace/config/types";
import { isStrategyDashboard } from "../../data/lovelace/config/types";
const lovelaceStruct = type({
title: optional(string()),
@@ -113,21 +112,7 @@ class LovelaceFullConfigEditor extends LitElement {
oldLovelace.rawConfig !== this.lovelace.rawConfig &&
!deepEqual(oldLovelace.rawConfig, this.lovelace.rawConfig)
) {
showToast(this, {
message: this.hass!.localize(
"ui.panel.lovelace.editor.raw_editor.lovelace_changed"
),
action: {
action: () => {
this.yamlEditor.value = dump(this.lovelace!.rawConfig);
},
text: this.hass!.localize(
"ui.panel.lovelace.editor.raw_editor.reload"
),
},
duration: -1,
dismissable: false,
});
this.yamlEditor.value = dump(this.lovelace!.rawConfig);
}
}

View File

@@ -631,8 +631,11 @@ class HUIRoot extends LitElement {
`;
}
private _handleWindowScroll = () => {
this.toggleAttribute("scrolled", window.scrollY !== 0);
private _handleContainerScroll = () => {
this.toggleAttribute(
"scrolled",
this._viewRoot ? this._viewRoot.scrollTop !== 0 : false
);
};
private _locationChanged = () => {
@@ -663,7 +666,7 @@ class HUIRoot extends LitElement {
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
window.addEventListener("scroll", this._handleWindowScroll, {
this._viewRoot?.addEventListener("scroll", this._handleContainerScroll, {
passive: true,
});
this._handleUrlChanged();
@@ -674,7 +677,7 @@ class HUIRoot extends LitElement {
public connectedCallback(): void {
super.connectedCallback();
window.addEventListener("scroll", this._handleWindowScroll, {
this._viewRoot?.addEventListener("scroll", this._handleContainerScroll, {
passive: true,
});
window.addEventListener("popstate", this._handlePopState);
@@ -685,10 +688,13 @@ class HUIRoot extends LitElement {
public disconnectedCallback(): void {
super.disconnectedCallback();
window.removeEventListener("scroll", this._handleWindowScroll);
this._viewRoot?.removeEventListener("scroll", this._handleContainerScroll);
window.removeEventListener("popstate", this._handlePopState);
window.removeEventListener("location-changed", this._locationChanged);
this.toggleAttribute("scrolled", window.scrollY !== 0);
this.toggleAttribute(
"scrolled",
this._viewRoot ? this._viewRoot.scrollTop !== 0 : false
);
// Re-enable history scroll restoration when leaving the page
window.history.scrollRestoration = "auto";
}
@@ -821,9 +827,11 @@ class HUIRoot extends LitElement {
(this._restoreScroll && this._viewScrollPositions[newSelectView]) ||
0;
this._restoreScroll = false;
requestAnimationFrame(() =>
scrollTo({ behavior: "auto", top: position })
);
requestAnimationFrame(() => {
if (this._viewRoot) {
this._viewRoot.scrollTo({ behavior: "auto", top: position });
}
});
}
this._selectView(newSelectView, force);
});
@@ -1148,7 +1156,7 @@ class HUIRoot extends LitElement {
const path = this.config.views[viewIndex].path || viewIndex;
this._navigateToView(path);
} else if (!this._editMode) {
scrollTo({ behavior: "smooth", top: 0 });
this._viewRoot?.scrollTo({ behavior: "smooth", top: 0 });
}
}
@@ -1159,7 +1167,7 @@ class HUIRoot extends LitElement {
// Save scroll position of current view
if (this._curView != null) {
this._viewScrollPositions[this._curView] = window.scrollY;
this._viewScrollPositions[this._curView] = this._viewRoot?.scrollTop ?? 0;
}
viewIndex = viewIndex === undefined ? 0 : viewIndex;
@@ -1467,9 +1475,14 @@ class HUIRoot extends LitElement {
hui-view-container {
position: relative;
display: flex;
min-height: 100vh;
height: calc(
100vh - var(--header-height) - var(--safe-area-inset-top) - var(
--view-container-padding-top,
0px
)
);
box-sizing: border-box;
padding-top: calc(
margin-top: calc(
var(--header-height) + var(--safe-area-inset-top) +
var(--view-container-padding-top, 0px)
);
@@ -1492,7 +1505,12 @@ class HUIRoot extends LitElement {
* In edit mode we have the tab bar on a new line *
*/
hui-view-container.has-tab-bar {
padding-top: calc(
height: calc(
100vh - var(--header-height, 56px) - calc(
var(--tab-bar-height, 56px) - 2px
) - var(--safe-area-inset-top, 0px)
);
margin-top: calc(
var(--header-height, 56px) +
calc(var(--tab-bar-height, 56px) - 2px) +
var(--safe-area-inset-top, 0px)

View File

@@ -418,11 +418,15 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
}
private _toggleView() {
// The scroll container is the hui-view-container parent
const scrollContainer = this.closest("hui-view-container");
const scrollTop = scrollContainer?.scrollTop ?? 0;
// Save current scroll position
if (this._sidebarTabActive) {
this._sidebarScrollTop = window.scrollY;
this._sidebarScrollTop = scrollTop;
} else {
this._contentScrollTop = window.scrollY;
this._contentScrollTop = scrollTop;
}
this._sidebarTabActive = !this._sidebarTabActive;
@@ -438,7 +442,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
const scrollY = this._sidebarTabActive
? this._sidebarScrollTop
: this._contentScrollTop;
window.scrollTo(0, scrollY);
scrollContainer?.scrollTo(0, scrollY);
});
}

View File

@@ -4,6 +4,7 @@ import { customElement, property, state } from "lit/decorators";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { listenMediaQuery } from "../../../common/dom/media_query";
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
import { haStyleScrollbar } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
type BackgroundConfig = LovelaceViewConfig["background"];
@@ -22,6 +23,7 @@ class HuiViewContainer extends LitElement {
public connectedCallback(): void {
super.connectedCallback();
this.classList.add("ha-scrollbar");
this._setUpMediaQuery();
this._applyTheme();
}
@@ -74,11 +76,16 @@ class HuiViewContainer extends LitElement {
}
}
static styles = css`
:host {
display: relative;
}
`;
static styles = [
haStyleScrollbar,
css`
:host {
display: block;
height: 100%;
-webkit-overflow-scrolling: touch;
}
`,
];
}
declare global {

View File

@@ -102,14 +102,6 @@ export const getMyRedirects = (): Redirects => ({
component: "zwave_js",
redirect: "/config/zwave_js/dashboard",
},
config_matter: {
component: "matter",
redirect: "/config/matter/dashboard",
},
config_thread: {
component: "thread",
redirect: "/config/thread",
},
add_zigbee_device: {
component: "zha",
redirect: "/config/zha/add",

View File

@@ -1,20 +1,19 @@
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../components/ha-card";
import "../../components/ha-button";
import "../../components/ha-spinner";
import "../../components/ha-textfield";
import "../../components/ha-password-field";
import { haStyle } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
import "../../components/ha-alert";
import "../../components/ha-button";
import "../../components/ha-card";
import "../../components/ha-input";
import "../../components/ha-spinner";
import { changePassword, deleteAllRefreshTokens } from "../../data/auth";
import type { RefreshToken } from "../../data/refresh_token";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../dialogs/generic/show-dialog-box";
import type { RefreshToken } from "../../data/refresh_token";
import { changePassword, deleteAllRefreshTokens } from "../../data/auth";
import { haStyle } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
@customElement("ha-change-password-card")
class HaChangePasswordCard extends LitElement {
@@ -47,8 +46,10 @@ class HaChangePasswordCard extends LitElement {
? html`<ha-alert alert-type="success">${this._statusMsg}</ha-alert>`
: ""}
<ha-password-field
<ha-input
id="currentPassword"
type="password"
password-toggle
name="currentPassword"
.label=${this.hass.localize(
"ui.panel.profile.change_password.current_password"
@@ -58,10 +59,12 @@ class HaChangePasswordCard extends LitElement {
@input=${this._currentPasswordChanged}
@change=${this._currentPasswordChanged}
required
></ha-password-field>
></ha-input>
${this._currentPassword
? html`<ha-password-field
? html`<ha-input
type="password"
password-toggle
.label=${this.hass.localize(
"ui.panel.profile.change_password.new_password"
)}
@@ -72,8 +75,10 @@ class HaChangePasswordCard extends LitElement {
@change=${this._newPasswordChanged}
required
autoValidate
></ha-password-field>
<ha-password-field
></ha-input>
<ha-input
type="password"
password-toggle
.label=${this.hass.localize(
"ui.panel.profile.change_password.confirm_new_password"
)}
@@ -84,7 +89,7 @@ class HaChangePasswordCard extends LitElement {
@change=${this._newPasswordConfirmChanged}
required
autoValidate
></ha-password-field>`
></ha-input>`
: ""}
</div>
@@ -195,10 +200,6 @@ class HaChangePasswordCard extends LitElement {
return [
haStyle,
css`
ha-textfield {
margin-top: 8px;
display: block;
}
#currentPassword {
margin-top: 0;
}

View File

@@ -186,24 +186,10 @@ export const haStyleDialog = css`
var(--safe-area-inset-right, 0) var(--safe-area-inset-bottom, 0)
var(--safe-area-inset-left, 0);
--vertical-align-dialog: flex-end;
}
ha-dialog {
--ha-dialog-border-radius: var(--ha-border-radius-square);
ha-dialog,
ha-adaptive-dialog {
--mdc-dialog-min-width: 100vw;
--mdc-dialog-max-width: 100vw;
--mdc-dialog-min-height: 100vh;
--mdc-dialog-min-height: 100svh;
--mdc-dialog-max-height: 100vh;
--mdc-dialog-max-height: 100svh;
--dialog-container-padding: 0px;
--dialog-surface-padding: var(--safe-area-inset-top, 0)
var(--safe-area-inset-right, 0) var(--safe-area-inset-bottom, 0)
var(--safe-area-inset-left, 0);
--vertical-align-dialog: flex-end;
}
ha-dialog {
--ha-dialog-border-radius: var(--ha-border-radius-square);
}
}
}
.error {
color: var(--error-color);
@@ -258,17 +244,20 @@ export const haStyleDialogFixedTop = css`
`;
export const haStyleScrollbar = css`
.ha-scrollbar::-webkit-scrollbar {
.ha-scrollbar::-webkit-scrollbar,
:host(.ha-scrollbar)::-webkit-scrollbar {
width: 0.4rem;
height: 0.4rem;
}
.ha-scrollbar::-webkit-scrollbar-thumb {
.ha-scrollbar::-webkit-scrollbar-thumb,
:host(.ha-scrollbar)::-webkit-scrollbar-thumb {
border-radius: var(--ha-border-radius-sm);
background: var(--scrollbar-thumb-color);
}
.ha-scrollbar {
.ha-scrollbar,
:host(.ha-scrollbar) {
overflow-y: auto;
scrollbar-color: var(--scrollbar-thumb-color) transparent;
scrollbar-width: thin;

View File

@@ -359,8 +359,6 @@ export const darkColorStyles = css`
--outline-hover-color: rgba(225, 225, 225, 0.24);
--shadow-color: rgba(0, 0, 0, 0.48);
--scrollbar-thumb-color: rgb(110, 110, 110);
--mdc-ripple-color: #aaaaaa;
--mdc-linear-progress-buffer-color: rgba(255, 255, 255, 0.1);

View File

@@ -64,5 +64,10 @@ export const waColorStyles = css`
--wa-focus-ring-color: var(--ha-color-neutral-60);
--wa-shadow-l: 4px 8px 12px 0 rgba(0, 0, 0, 0.3);
--wa-form-control-background-color: var(--wa-color-surface-raised);
--wa-form-control-border-color: var(--ha-color-border-neutral-quiet);
--wa-form-control-value-color: var(--primary-text-color);
--wa-form-control-placeholder-color: var(--ha-color-text-secondary);
}
`;

View File

@@ -51,21 +51,6 @@ export const mainStyles = css`
/* dialog backdrop filter */
--ha-dialog-scrim-backdrop-filter: brightness(68%);
/* scrollbar */
scrollbar-color: var(--scrollbar-thumb-color) transparent;
scrollbar-width: thin;
}
html::-webkit-scrollbar {
width: 0.4rem;
height: 0.4rem;
}
html::-webkit-scrollbar-thumb {
border-radius: var(--ha-border-radius-sm);
background: var(--scrollbar-thumb-color);
border: 3px solid transparent;
}
`;

View File

@@ -14,10 +14,8 @@ export const waMainStyles = css`
--wa-space-l: var(--ha-space-6);
--wa-space-xl: var(--ha-space-8);
--wa-form-control-padding-block: 0.75em;
--wa-form-control-value-line-height: var(--ha-line-height-condensed);
--wa-font-weight-action: var(--ha-font-weight-medium);
--wa-font-weight-body: var(--ha-font-weight-normal);
--wa-transition-normal: 150ms;
--wa-transition-fast: 75ms;
--wa-transition-easing: ease;
@@ -29,13 +27,25 @@ export const waMainStyles = css`
--wa-border-radius-s: var(--ha-border-radius-sm);
--wa-border-radius-m: var(--ha-border-radius-md);
--wa-border-radius-l: var(--ha-border-radius-lg);
--wa-border-radius-pill: var(--ha-border-radius-pill);
--wa-line-height-condensed: var(--ha-line-height-condensed);
--wa-font-size-s: var(--ha-font-size-s);
--wa-font-size-m: var(--ha-font-size-m);
--wa-font-size-l: var(--ha-font-size-l);
--wa-shadow-s: var(--ha-box-shadow-s);
--wa-shadow-m: var(--ha-box-shadow-m);
--wa-shadow-l: var(--ha-box-shadow-l);
--wa-form-control-padding-block: 0.75em;
--wa-form-control-value-line-height: var(--wa-line-height-condensed);
--wa-form-control-value-font-weight: var(--wa-font-weight-body);
--wa-form-control-border-radius: var(--wa-border-radius-l);
--wa-form-control-border-style: var(--wa-border-style);
--wa-form-control-border-width: var(--wa-border-width-s);
--wa-form-control-height: 40px;
--wa-form-control-padding-inline: var(--ha-space-3);
}
${scrollLockStyles}

View File

@@ -7041,7 +7041,7 @@
"remove_node": "Remove foreign device",
"remove_a_node": "Remove a device",
"rebuild_network_routes": "Discover and assign new routes",
"in_progress_inclusion_exclusion": "Z-Wave JS is searching for devices",
"in_progress_inclusion_exclusion": "Z-Wave is searching for devices",
"cancel_inclusion_exclusion": "Stop searching"
},
"dashboard": {
@@ -7516,7 +7516,7 @@
"caption": "Logs",
"title": "Z-Wave logs",
"log_level": "Log level",
"subscribed_to_logs": "Subscribed to Z-Wave JS log messages…",
"subscribed_to_logs": "Subscribed to Z-Wave log messages…",
"log_level_changed": "Log level changed to: {level}",
"download_logs": "Download logs"
},
@@ -7628,7 +7628,7 @@
},
"picker": {
"title": "Select Z-Wave network",
"no_entries": "No Z-Wave networks configured. Set up the Z-Wave JS integration first."
"no_entries": "No Z-Wave networks configured. Set up the Z-Wave integration first."
}
},
"matter": {
@@ -8352,7 +8352,6 @@
"unsaved_changes": "Unsaved changes",
"saved": "Saved",
"reload": "Reload",
"lovelace_changed": "Your dashboard was updated, do you want to load the updated config in the editor and lose your current changes?",
"confirm_reset_config_title": "Reset dashboard configuration?",
"confirm_reset_config_text": "Your dashboard will be reset to an empty state. You can start fresh and build your dashboard from scratch.",
"confirm_unsaved_changes": "You have unsaved changes, are you sure you want to exit?",
@@ -9675,8 +9674,8 @@
"entity_unavailable": "Entity is currently unavailable: {entity}",
"starting": "Home Assistant is starting. Not everything may be available yet."
},
"changed_toast": {
"message": "Your dashboard was updated. Refresh to see changes?"
"externally_updated_toast": {
"message": "Dashboard updated in another session. Refreshing will discard your unsaved changes."
},
"components": {
"timestamp-display": {

391
yarn.lock
View File

@@ -25,29 +25,29 @@ __metadata:
languageName: node
linkType: hard
"@asamuzakjp/css-color@npm:^4.1.1":
version: 4.1.1
resolution: "@asamuzakjp/css-color@npm:4.1.1"
"@asamuzakjp/css-color@npm:^4.1.2":
version: 4.1.2
resolution: "@asamuzakjp/css-color@npm:4.1.2"
dependencies:
"@csstools/css-calc": "npm:^2.1.4"
"@csstools/css-color-parser": "npm:^3.1.0"
"@csstools/css-parser-algorithms": "npm:^3.0.5"
"@csstools/css-tokenizer": "npm:^3.0.4"
lru-cache: "npm:^11.2.4"
checksum: 10/4b7e900d9d18a86e01a42d6140936a0373801e44854bcdd70fc7c06022b7e6a793d1960fbe9f6a65bdad1874798a30c0cd91e52bfd5eae909b3347c98cc1c4f5
"@csstools/css-calc": "npm:^3.0.0"
"@csstools/css-color-parser": "npm:^4.0.1"
"@csstools/css-parser-algorithms": "npm:^4.0.0"
"@csstools/css-tokenizer": "npm:^4.0.0"
lru-cache: "npm:^11.2.5"
checksum: 10/0938a4598a1d06d4db53b8aff406815f77047419eccb78f484dd26d13bd6cafaff247bc42f5493f2cb585477f461a38fba0db3c7a407ba9f281d27bc0d8f1983
languageName: node
linkType: hard
"@asamuzakjp/dom-selector@npm:^6.7.6":
version: 6.7.6
resolution: "@asamuzakjp/dom-selector@npm:6.7.6"
"@asamuzakjp/dom-selector@npm:^6.8.1":
version: 6.8.1
resolution: "@asamuzakjp/dom-selector@npm:6.8.1"
dependencies:
"@asamuzakjp/nwsapi": "npm:^2.3.9"
bidi-js: "npm:^1.0.3"
css-tree: "npm:^3.1.0"
is-potential-custom-element-name: "npm:^1.0.1"
lru-cache: "npm:^11.2.4"
checksum: 10/91a479f5f59a3b1b23f46407d874882c05b50e72316e57af105c88603fb1008120c46c7fe7504c5be556ea4c9a68ee10aee43433bd8d34f000b9aaf6dcae4d5c
lru-cache: "npm:^11.2.6"
checksum: 10/4d1c63bf094aa35c9c60ad8d2faf45ee4f5f8d1520fbb158e2552c456f8264029932ff4464ea18ea760a89b3075b4bf70e43b2086191d256f35eff46fde3eb24
languageName: node
linkType: hard
@@ -1204,6 +1204,17 @@ __metadata:
languageName: node
linkType: hard
"@bramus/specificity@npm:^2.4.2":
version: 2.4.2
resolution: "@bramus/specificity@npm:2.4.2"
dependencies:
css-tree: "npm:^3.0.0"
bin:
specificity: bin/cli.js
checksum: 10/4255ed6ff12f7db9ec3c21acfd0da2327d30ec29deb199345810cdcad992618f40039c5483eefeb665913bffbc80b690e9f1b954fbbbfa93480c6a22f9c3a69c
languageName: node
linkType: hard
"@bundle-stats/plugin-webpack-filter@npm:4.21.10":
version: 4.21.10
resolution: "@bundle-stats/plugin-webpack-filter@npm:4.21.10"
@@ -1294,56 +1305,56 @@ __metadata:
languageName: node
linkType: hard
"@csstools/color-helpers@npm:^5.1.0":
version: 5.1.0
resolution: "@csstools/color-helpers@npm:5.1.0"
checksum: 10/0138b3d5ccbe77aeccf6721fd008a53523c70e932f0c82dca24a1277ca780447e1d8357da47512ebf96358476f8764de57002f3e491920d67e69202f5a74c383
"@csstools/color-helpers@npm:^6.0.2":
version: 6.0.2
resolution: "@csstools/color-helpers@npm:6.0.2"
checksum: 10/c47a943e947d76980d0e1071027cb70481ac481968e744a05a7aea7ede9886f10d062b2e3691e03c115d97b053d4140c1ca28e24c1ffe2d21693e126de6522e9
languageName: node
linkType: hard
"@csstools/css-calc@npm:^2.1.4":
version: 2.1.4
resolution: "@csstools/css-calc@npm:2.1.4"
"@csstools/css-calc@npm:^3.0.0, @csstools/css-calc@npm:^3.1.1":
version: 3.1.1
resolution: "@csstools/css-calc@npm:3.1.1"
peerDependencies:
"@csstools/css-parser-algorithms": ^3.0.5
"@csstools/css-tokenizer": ^3.0.4
checksum: 10/06975b650c0f44c60eeb7afdb3fd236f2dd607b2c622e0bc908d3f54de39eb84e0692833320d03dac04bd6c1ab0154aa3fa0dd442bd9e5f917cf14d8e2ba8d74
"@csstools/css-parser-algorithms": ^4.0.0
"@csstools/css-tokenizer": ^4.0.0
checksum: 10/faa3aa2736b20757ceafd76e3d2841e8726ec9e7ae78e387684eb462aba73d533ba384039338685c3a52196196300ccdfecb051e59864b1d3b457fe927b7f53b
languageName: node
linkType: hard
"@csstools/css-color-parser@npm:^3.1.0":
version: 3.1.0
resolution: "@csstools/css-color-parser@npm:3.1.0"
"@csstools/css-color-parser@npm:^4.0.1":
version: 4.0.2
resolution: "@csstools/css-color-parser@npm:4.0.2"
dependencies:
"@csstools/color-helpers": "npm:^5.1.0"
"@csstools/css-calc": "npm:^2.1.4"
"@csstools/color-helpers": "npm:^6.0.2"
"@csstools/css-calc": "npm:^3.1.1"
peerDependencies:
"@csstools/css-parser-algorithms": ^3.0.5
"@csstools/css-tokenizer": ^3.0.4
checksum: 10/4741095fdc4501e8e7ada4ed14fbf9dbbe6fea9b989818790ebca15657c29c62defbebacf18592cde2aa638a1d098bbe86d742d2c84ba932fbc00fac51cb8805
"@csstools/css-parser-algorithms": ^4.0.0
"@csstools/css-tokenizer": ^4.0.0
checksum: 10/6418bfadc8c15d3a65c1e80278df383b542f0437446c0ba21d591dd564bcc19ab0b11243edf62672f4c62cc778f9b386fa4349e9a8d1de2b414148ea8a1ac775
languageName: node
linkType: hard
"@csstools/css-parser-algorithms@npm:^3.0.5":
version: 3.0.5
resolution: "@csstools/css-parser-algorithms@npm:3.0.5"
"@csstools/css-parser-algorithms@npm:^4.0.0":
version: 4.0.0
resolution: "@csstools/css-parser-algorithms@npm:4.0.0"
peerDependencies:
"@csstools/css-tokenizer": ^3.0.4
checksum: 10/e93083b5cb36a3c1e7a47ce10cf62961d05bd1e4c608bb3ee50186ff740157ab0ec16a3956f7b86251efd10703034d849693201eea858ae904848c68d2d46ada
"@csstools/css-tokenizer": ^4.0.0
checksum: 10/000f3ba55f440d9fbece50714e88f9d4479e2bde9e0568333492663f2c6034dc31d0b9ef5d66d196c76be58eea145ca6920aa8bdfdcc6361894806c21b5402d0
languageName: node
linkType: hard
"@csstools/css-syntax-patches-for-csstree@npm:^1.0.21":
version: 1.0.25
resolution: "@csstools/css-syntax-patches-for-csstree@npm:1.0.25"
checksum: 10/42dfcd164ed6a66eee8dd3fcdbdaa58d032ab8c3bb5ead2453915429367766879b25332e09379a67357fd33742b856160e7c0182c7d90be00f57571b916c18e7
"@csstools/css-syntax-patches-for-csstree@npm:^1.0.26":
version: 1.0.28
resolution: "@csstools/css-syntax-patches-for-csstree@npm:1.0.28"
checksum: 10/b7b393a4285b2d91aec71ef714ebe356f6607d6358e025306eaf567bd7bbd201e3a5d1f0a8d3cd9063b3dd40bbdbfd6664b4c658c2bca45c4c1b122a54a0804e
languageName: node
linkType: hard
"@csstools/css-tokenizer@npm:^3.0.4":
version: 3.0.4
resolution: "@csstools/css-tokenizer@npm:3.0.4"
checksum: 10/eb6c84c086312f6bb8758dfe2c85addd7475b0927333c5e39a4d59fb210b9810f8c346972046f95e60a721329cffe98895abe451e51de753ad1ca7a8c24ec65f
"@csstools/css-tokenizer@npm:^4.0.0":
version: 4.0.0
resolution: "@csstools/css-tokenizer@npm:4.0.0"
checksum: 10/074ade1a7fc3410b813c8982cf07a56814a55af509c533c2dc80d5689f34d2ba38219f8fa78fa36ea2adc6c5db506ea3c3a667388dda1b59b1281fdd2a2d1e28
languageName: node
linkType: hard
@@ -1962,9 +1973,9 @@ __metadata:
languageName: node
linkType: hard
"@home-assistant/webawesome@npm:3.2.1-ha.2":
version: 3.2.1-ha.2
resolution: "@home-assistant/webawesome@npm:3.2.1-ha.2"
"@home-assistant/webawesome@npm:3.2.1-ha.3":
version: 3.2.1-ha.3
resolution: "@home-assistant/webawesome@npm:3.2.1-ha.3"
dependencies:
"@ctrl/tinycolor": "npm:4.1.0"
"@floating-ui/dom": "npm:^1.6.13"
@@ -1975,7 +1986,7 @@ __metadata:
lit: "npm:^3.2.1"
nanoid: "npm:^5.1.5"
qr-creator: "npm:^1.0.0"
checksum: 10/36f15a10760fe4c13c9551e4440d9ad395c894d6f3735494da2032948fae052056ea6e762fbfa3b2e7e0cb94a8d1e214bb73478e682813cb3ed9c44b2daec98d
checksum: 10/e5182d4ce11ed6a0b17e6da7783f0dea20dc743593b9873cbfcaf26425a78383e9f90dbb152618b96422ea37be6fb8ff09aa9903b012d5fe0ab5ebc0570154a4
languageName: node
linkType: hard
@@ -4321,12 +4332,12 @@ __metadata:
languageName: node
linkType: hard
"@swc/helpers@npm:0.5.18":
version: 0.5.18
resolution: "@swc/helpers@npm:0.5.18"
"@swc/helpers@npm:0.5.19":
version: 0.5.19
resolution: "@swc/helpers@npm:0.5.19"
dependencies:
tslib: "npm:^2.8.0"
checksum: 10/03c7efa3e62d965fddd0baea98ee7a4c3ba7fa58187f07f26ec8d86740572f5ffd6f7517578a1d3201f64c85399be1538eba4dd30cef79d073060ecb101d753c
checksum: 10/3fd365fb3265f97e1241bcbcea9bfa5e15e03c630424e1b54597e00d30be2c271cb0c74f45e1739c6bc5ae892647302fab412de5138941aa96e66aebf4586700
languageName: node
linkType: hard
@@ -5013,13 +5024,12 @@ __metadata:
languageName: node
linkType: hard
"@types/tar@npm:6.1.13":
version: 6.1.13
resolution: "@types/tar@npm:6.1.13"
"@types/tar@npm:7.0.87":
version: 7.0.87
resolution: "@types/tar@npm:7.0.87"
dependencies:
"@types/node": "npm:*"
minipass: "npm:^4.0.0"
checksum: 10/d325223cf90399fd03f366d0eabe2383e75e550b3e40a006d5f062d006b894a475cd7c0968d258a8eb8eae5df30b6e7f4607d493a474f89134bbff65362b77ed
tar: "npm:*"
checksum: 10/f53a63d49d1775928a60afee5524c8d58052cf0896c0e051461f4faf4131caa0cec1d2e3d711374d91d55a59de3e898c43243e5026e5faca9436b69cb5e414b0
languageName: node
linkType: hard
@@ -5053,105 +5063,105 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:8.54.0":
version: 8.54.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.54.0"
"@typescript-eslint/eslint-plugin@npm:8.56.0":
version: 8.56.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.56.0"
dependencies:
"@eslint-community/regexpp": "npm:^4.12.2"
"@typescript-eslint/scope-manager": "npm:8.54.0"
"@typescript-eslint/type-utils": "npm:8.54.0"
"@typescript-eslint/utils": "npm:8.54.0"
"@typescript-eslint/visitor-keys": "npm:8.54.0"
"@typescript-eslint/scope-manager": "npm:8.56.0"
"@typescript-eslint/type-utils": "npm:8.56.0"
"@typescript-eslint/utils": "npm:8.56.0"
"@typescript-eslint/visitor-keys": "npm:8.56.0"
ignore: "npm:^7.0.5"
natural-compare: "npm:^1.4.0"
ts-api-utils: "npm:^2.4.0"
peerDependencies:
"@typescript-eslint/parser": ^8.54.0
eslint: ^8.57.0 || ^9.0.0
"@typescript-eslint/parser": ^8.56.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/8f1c74ac77d7a84ae3f201bb09cb67271662befed036266af1eaa0653d09b545353441640516c1c86e0a94939887d32f0473c61a642488b14d46533742bfbd1b
checksum: 10/44201eae518c759cf3110f7e0a374372ef22bffa3cca61685aebe916b06bcb5f3ed6ffedba252199dca0006dfc22c54b132cd0337fd15e8c083eda22f9cf6b0c
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:8.54.0":
version: 8.54.0
resolution: "@typescript-eslint/parser@npm:8.54.0"
"@typescript-eslint/parser@npm:8.56.0":
version: 8.56.0
resolution: "@typescript-eslint/parser@npm:8.56.0"
dependencies:
"@typescript-eslint/scope-manager": "npm:8.54.0"
"@typescript-eslint/types": "npm:8.54.0"
"@typescript-eslint/typescript-estree": "npm:8.54.0"
"@typescript-eslint/visitor-keys": "npm:8.54.0"
"@typescript-eslint/scope-manager": "npm:8.56.0"
"@typescript-eslint/types": "npm:8.56.0"
"@typescript-eslint/typescript-estree": "npm:8.56.0"
"@typescript-eslint/visitor-keys": "npm:8.56.0"
debug: "npm:^4.4.3"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/d2e09462c9966ef3deeba71d9e41d1d4876c61eea65888c93a3db6fba48b89a2165459c6519741d40e969da05ed98d3f4c87a7f56c5521ab5699743cc315f6cb
checksum: 10/9bdb2c7915665a1031499049974997020bbc34557803a8c3718b323d5583a3fdfc27797ec714b617743225c8dce9ab589eb442b71c435b464ad45365a6fbba57
languageName: node
linkType: hard
"@typescript-eslint/project-service@npm:8.54.0":
version: 8.54.0
resolution: "@typescript-eslint/project-service@npm:8.54.0"
"@typescript-eslint/project-service@npm:8.56.0":
version: 8.56.0
resolution: "@typescript-eslint/project-service@npm:8.56.0"
dependencies:
"@typescript-eslint/tsconfig-utils": "npm:^8.54.0"
"@typescript-eslint/types": "npm:^8.54.0"
"@typescript-eslint/tsconfig-utils": "npm:^8.56.0"
"@typescript-eslint/types": "npm:^8.56.0"
debug: "npm:^4.4.3"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10/93f0483f6bbcf7cf776a53a130f7606f597fba67cf111e1897873bf1531efaa96e4851cfd461da0f0cc93afbdb51e47bcce11cf7dd4fb68b7030c7f9f240b92f
checksum: 10/b46cc78bfb50ee84cb12e2e99c1a3d9606161980cf56ba33be6244ccff2ba4c78ceec46706ad597508fda167e0f965d849c1c516400ad41259027be3fbd815eb
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:8.54.0":
version: 8.54.0
resolution: "@typescript-eslint/scope-manager@npm:8.54.0"
"@typescript-eslint/scope-manager@npm:8.56.0":
version: 8.56.0
resolution: "@typescript-eslint/scope-manager@npm:8.56.0"
dependencies:
"@typescript-eslint/types": "npm:8.54.0"
"@typescript-eslint/visitor-keys": "npm:8.54.0"
checksum: 10/3474f3197e8647754393dee62b3145c9de71eaa66c8a68f61c8283aa332141803885db9c96caa6a51f78128ad9ef92f774a90361655e57bd951d5b57eb76f914
"@typescript-eslint/types": "npm:8.56.0"
"@typescript-eslint/visitor-keys": "npm:8.56.0"
checksum: 10/3662355120ea8e21ce01c999decbd2a09fe4edb1c01e376fe347952d968ecfedff99b9484334e133e41284a15f2e1bc8efd490b1e73a16980614445c25b07b0d
languageName: node
linkType: hard
"@typescript-eslint/tsconfig-utils@npm:8.54.0, @typescript-eslint/tsconfig-utils@npm:^8.54.0":
version: 8.54.0
resolution: "@typescript-eslint/tsconfig-utils@npm:8.54.0"
"@typescript-eslint/tsconfig-utils@npm:8.56.0, @typescript-eslint/tsconfig-utils@npm:^8.56.0":
version: 8.56.0
resolution: "@typescript-eslint/tsconfig-utils@npm:8.56.0"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10/e9d6b29538716f007919bfcee94f09b7f8e7d2b684ad43d1a3c8d43afb9f0539c7707f84a34f42054e31c8c056b0ccf06575d89e860b4d34632ffefaefafe1fc
checksum: 10/b1834aeffcdc07835eae0bf52aca573cba7e6528b5c1483e9b1f7f4f9e1f6450a8650796be11140e0437caf7eb1b0f9711c22989c8294547534f12614a759760
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:8.54.0":
version: 8.54.0
resolution: "@typescript-eslint/type-utils@npm:8.54.0"
"@typescript-eslint/type-utils@npm:8.56.0":
version: 8.56.0
resolution: "@typescript-eslint/type-utils@npm:8.56.0"
dependencies:
"@typescript-eslint/types": "npm:8.54.0"
"@typescript-eslint/typescript-estree": "npm:8.54.0"
"@typescript-eslint/utils": "npm:8.54.0"
"@typescript-eslint/types": "npm:8.56.0"
"@typescript-eslint/typescript-estree": "npm:8.56.0"
"@typescript-eslint/utils": "npm:8.56.0"
debug: "npm:^4.4.3"
ts-api-utils: "npm:^2.4.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/60e92fb32274abd70165ce6f4187e4cffa55416374c63731d7de8fdcfb7a558b4dd48909ff1ad38ac39d2ea1248ec54d6ce38dbc065fd34529a217fc2450d5b1
checksum: 10/f272b9acc004f125cbf0df18265a43ba50cd3666262afc663585acdd1be6b6b30724bd8cf4cd5aa2757b7f10ceafa92fd1af30c1931fb22ac38521eda7f79c89
languageName: node
linkType: hard
"@typescript-eslint/types@npm:8.54.0, @typescript-eslint/types@npm:^8.54.0":
version: 8.54.0
resolution: "@typescript-eslint/types@npm:8.54.0"
checksum: 10/c25cc0bdf90fb150cf6ce498897f43fe3adf9e872562159118f34bd91a9bfab5f720cb1a41f3cdf253b2e840145d7d372089b7cef5156624ef31e98d34f91b31
"@typescript-eslint/types@npm:8.56.0, @typescript-eslint/types@npm:^8.56.0":
version: 8.56.0
resolution: "@typescript-eslint/types@npm:8.56.0"
checksum: 10/d7549535c99d9202742bf0191bcc2822c2d18a03e206be9ad5a6f6b0902de7381c93e8c238754fe5d1dfdcc22d7e3bbafa032f63ba165d6dc03e180f84b138f9
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:8.54.0":
version: 8.54.0
resolution: "@typescript-eslint/typescript-estree@npm:8.54.0"
"@typescript-eslint/typescript-estree@npm:8.56.0":
version: 8.56.0
resolution: "@typescript-eslint/typescript-estree@npm:8.56.0"
dependencies:
"@typescript-eslint/project-service": "npm:8.54.0"
"@typescript-eslint/tsconfig-utils": "npm:8.54.0"
"@typescript-eslint/types": "npm:8.54.0"
"@typescript-eslint/visitor-keys": "npm:8.54.0"
"@typescript-eslint/project-service": "npm:8.56.0"
"@typescript-eslint/tsconfig-utils": "npm:8.56.0"
"@typescript-eslint/types": "npm:8.56.0"
"@typescript-eslint/visitor-keys": "npm:8.56.0"
debug: "npm:^4.4.3"
minimatch: "npm:^9.0.5"
semver: "npm:^7.7.3"
@@ -5159,32 +5169,32 @@ __metadata:
ts-api-utils: "npm:^2.4.0"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10/3a545037c6f9319251d3ba44cf7a3216b1372422469e27f7ed3415244ebf42553da1ab4644da42d3f0ae2706a8cad12529ffebcb2e75406f74e3b30b812d010d
checksum: 10/55c8cfc7e265f320d780e69a677838821225fad8b853108ce2095f01509bf2ee8943280df9ac75560ed86265ef0e0a979ae4cb375d712f648b336032de79d19b
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:8.54.0":
version: 8.54.0
resolution: "@typescript-eslint/utils@npm:8.54.0"
"@typescript-eslint/utils@npm:8.56.0":
version: 8.56.0
resolution: "@typescript-eslint/utils@npm:8.56.0"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.9.1"
"@typescript-eslint/scope-manager": "npm:8.54.0"
"@typescript-eslint/types": "npm:8.54.0"
"@typescript-eslint/typescript-estree": "npm:8.54.0"
"@typescript-eslint/scope-manager": "npm:8.56.0"
"@typescript-eslint/types": "npm:8.56.0"
"@typescript-eslint/typescript-estree": "npm:8.56.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/9f88a2a7ab3e11aa0ff7f99c0e66a0cf2cba10b640def4c64a4f4ef427fecfb22f28dbe5697535915eb01f6507515ac43e45e0ff384bf82856e3420194d9ffdd
checksum: 10/f357bd15fe568cba0b89371e9a724eda38d78361a21dc0c4f49b0af4a23a140c77e2a8c6285c6fe8d8277e256a8a137aef7bcf6d97428eecd0c6e72ef08849ae
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:8.54.0":
version: 8.54.0
resolution: "@typescript-eslint/visitor-keys@npm:8.54.0"
"@typescript-eslint/visitor-keys@npm:8.56.0":
version: 8.56.0
resolution: "@typescript-eslint/visitor-keys@npm:8.56.0"
dependencies:
"@typescript-eslint/types": "npm:8.54.0"
eslint-visitor-keys: "npm:^4.2.1"
checksum: 10/cca5380ee30250302ee1459e5a0a38de8c16213026dbbff3d167fa7d71d012f31d60ac4483ad45ebd13f2ac963d1ca52dd5f22759a68d4ee57626e421769187a
"@typescript-eslint/types": "npm:8.56.0"
eslint-visitor-keys: "npm:^5.0.0"
checksum: 10/1eaa26ffe8a2c83d42d428beef207d793aef73c2e306f94e716d39519eaaa07547da898c7d63a5c406a3662895d735b4b1f33b513a1addb69473c65e7d92a2b5
languageName: node
linkType: hard
@@ -6924,7 +6934,7 @@ __metadata:
languageName: node
linkType: hard
"css-tree@npm:^3.1.0":
"css-tree@npm:^3.0.0, css-tree@npm:^3.1.0":
version: 3.1.0
resolution: "css-tree@npm:3.1.0"
dependencies:
@@ -6941,15 +6951,15 @@ __metadata:
languageName: node
linkType: hard
"cssstyle@npm:^5.3.7":
version: 5.3.7
resolution: "cssstyle@npm:5.3.7"
"cssstyle@npm:^6.0.1":
version: 6.0.2
resolution: "cssstyle@npm:6.0.2"
dependencies:
"@asamuzakjp/css-color": "npm:^4.1.1"
"@csstools/css-syntax-patches-for-csstree": "npm:^1.0.21"
"@asamuzakjp/css-color": "npm:^4.1.2"
"@csstools/css-syntax-patches-for-csstree": "npm:^1.0.26"
css-tree: "npm:^3.1.0"
lru-cache: "npm:^11.2.4"
checksum: 10/bd4469af81f068537dbbce53c4247f192e91202c19abc066b77b4ee7bbf256526bc82471198bec762ac70ea53ce17b8044aec69fd7982d2d0fd9fd7780329e2d
lru-cache: "npm:^11.2.5"
checksum: 10/88f102b618d1b86fba9fe967f5252fde090f8eea45f5f46bfbedd61a4839bc1dab85287ffc14e912af0f9a80a86c904654b9f6c04c09521aa147d70b1b9b5917
languageName: node
linkType: hard
@@ -7976,15 +7986,15 @@ __metadata:
languageName: node
linkType: hard
"eslint-plugin-wc@npm:3.0.2":
version: 3.0.2
resolution: "eslint-plugin-wc@npm:3.0.2"
"eslint-plugin-wc@npm:3.1.0":
version: 3.1.0
resolution: "eslint-plugin-wc@npm:3.1.0"
dependencies:
is-valid-element-name: "npm:^1.0.0"
js-levenshtein-esm: "npm:^2.0.0"
peerDependencies:
eslint: ">=8.40.0"
checksum: 10/488d8cbfb57c8845ff82358a7b5c1b2b6a670f2638ad577cdfb4ebf4270783624b6754c75223f0cec5fe1bf3273b62b1837e1bef1435c4376b14f1ec709b838e
checksum: 10/00797dcbf3f50d88ce1036555bc8940d7796015a77c2a5e9953638ae9d847f8a81027342232909924f46a1b834be957ed5e08168932482b9c21e6a0ef02f7543
languageName: node
linkType: hard
@@ -8019,6 +8029,13 @@ __metadata:
languageName: node
linkType: hard
"eslint-visitor-keys@npm:^5.0.0":
version: 5.0.1
resolution: "eslint-visitor-keys@npm:5.0.1"
checksum: 10/f9cc1a57b75e0ef949545cac33d01e8367e302de4c1483266ed4d8646ee5c306376660196bbb38b004e767b7043d1e661cb4336b49eff634a1bbe75c1db709ec
languageName: node
linkType: hard
"eslint@npm:9.39.3, eslint@npm:^9.39.1":
version: 9.39.3
resolution: "eslint@npm:9.39.3"
@@ -9174,7 +9191,7 @@ __metadata:
"@fullcalendar/list": "npm:6.1.20"
"@fullcalendar/luxon3": "npm:6.1.20"
"@fullcalendar/timegrid": "npm:6.1.20"
"@home-assistant/webawesome": "npm:3.2.1-ha.2"
"@home-assistant/webawesome": "npm:3.2.1-ha.3"
"@html-eslint/eslint-plugin": "npm:0.56.0"
"@lezer/highlight": "npm:1.2.3"
"@lit-labs/motion": "npm:1.1.0"
@@ -9213,7 +9230,7 @@ __metadata:
"@rsdoctor/rspack-plugin": "npm:1.5.2"
"@rspack/core": "npm:1.7.6"
"@rspack/dev-server": "npm:1.2.1"
"@swc/helpers": "npm:0.5.18"
"@swc/helpers": "npm:0.5.19"
"@thomasloven/round-slider": "npm:0.6.0"
"@tsparticles/engine": "npm:3.9.1"
"@tsparticles/preset-links": "npm:3.2.0"
@@ -9232,7 +9249,7 @@ __metadata:
"@types/mocha": "npm:10.0.10"
"@types/qrcode": "npm:1.5.6"
"@types/sortablejs": "npm:1.15.9"
"@types/tar": "npm:6.1.13"
"@types/tar": "npm:7.0.87"
"@types/ua-parser-js": "npm:0.7.39"
"@types/webspeechapi": "npm:0.0.29"
"@vibrant/color": "npm:4.0.4"
@@ -9265,7 +9282,7 @@ __metadata:
eslint-plugin-lit: "npm:2.2.1"
eslint-plugin-lit-a11y: "npm:5.1.1"
eslint-plugin-unused-imports: "npm:4.4.1"
eslint-plugin-wc: "npm:3.0.2"
eslint-plugin-wc: "npm:3.1.0"
fancy-log: "npm:2.0.0"
fs-extra: "npm:11.3.3"
fuse.js: "npm:7.1.0"
@@ -9283,7 +9300,7 @@ __metadata:
idb-keyval: "npm:6.2.2"
intl-messageformat: "npm:11.1.2"
js-yaml: "npm:4.1.1"
jsdom: "npm:28.0.0"
jsdom: "npm:28.1.0"
jszip: "npm:3.10.1"
leaflet: "npm:1.9.4"
leaflet-draw: "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
@@ -9313,14 +9330,14 @@ __metadata:
sortablejs: "patch:sortablejs@npm%3A1.15.6#~/.yarn/patches/sortablejs-npm-1.15.6-3235a8f83b.patch"
stacktrace-js: "npm:2.0.2"
superstruct: "npm:2.0.2"
tar: "npm:7.5.8"
tar: "npm:7.5.9"
terser-webpack-plugin: "npm:5.3.16"
tinykeys: "npm:3.0.0"
ts-lit-plugin: "npm:2.0.2"
typescript: "npm:5.9.3"
typescript-eslint: "npm:8.54.0"
typescript-eslint: "npm:8.56.0"
ua-parser-js: "npm:2.0.9"
vite-tsconfig-paths: "npm:6.0.5"
vite-tsconfig-paths: "npm:6.1.1"
vitest: "npm:4.0.18"
vue: "npm:2.7.16"
vue2-daterange-picker: "npm:0.6.8"
@@ -10341,14 +10358,15 @@ __metadata:
languageName: node
linkType: hard
"jsdom@npm:28.0.0":
version: 28.0.0
resolution: "jsdom@npm:28.0.0"
"jsdom@npm:28.1.0":
version: 28.1.0
resolution: "jsdom@npm:28.1.0"
dependencies:
"@acemir/cssom": "npm:^0.9.31"
"@asamuzakjp/dom-selector": "npm:^6.7.6"
"@asamuzakjp/dom-selector": "npm:^6.8.1"
"@bramus/specificity": "npm:^2.4.2"
"@exodus/bytes": "npm:^1.11.0"
cssstyle: "npm:^5.3.7"
cssstyle: "npm:^6.0.1"
data-urls: "npm:^7.0.0"
decimal.js: "npm:^10.6.0"
html-encoding-sniffer: "npm:^6.0.0"
@@ -10359,7 +10377,7 @@ __metadata:
saxes: "npm:^6.0.0"
symbol-tree: "npm:^3.2.4"
tough-cookie: "npm:^6.0.0"
undici: "npm:^7.20.0"
undici: "npm:^7.21.0"
w3c-xmlserializer: "npm:^5.0.0"
webidl-conversions: "npm:^8.0.1"
whatwg-mimetype: "npm:^5.0.0"
@@ -10370,7 +10388,7 @@ __metadata:
peerDependenciesMeta:
canvas:
optional: true
checksum: 10/c50461190982834446308bbfdfbbb829c6447b1693b1f0dd90fd717960a1d02c326cc90c9a378831d9bf8166702e57dcbf12691ddd7406c13d0a522c7b8971d9
checksum: 10/700ef06cf3a72998173205e49c7565926c22f51f562400ec033d426fe0a419f4209c3527735b8dd22eddef9798c905810600b89c84c3474447819fa8b37848ab
languageName: node
linkType: hard
@@ -10797,10 +10815,10 @@ __metadata:
languageName: node
linkType: hard
"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1, lru-cache@npm:^11.2.4":
version: 11.2.4
resolution: "lru-cache@npm:11.2.4"
checksum: 10/3b2da74c0b6653767f8164c38c4c4f4d7f0cc10c62bfa512663d94a830191ae6a5af742a8d88a8b30d5f9974652d3adae53931f32069139ad24fa2a18a199aca
"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1, lru-cache@npm:^11.2.5, lru-cache@npm:^11.2.6":
version: 11.2.6
resolution: "lru-cache@npm:11.2.6"
checksum: 10/91222bbd59f793a0a0ad57789388f06b34ac9bb1613433c1d1810457d09db5cd3ec8943227ce2e1f5d6a0a15d6f1a9f129cb2c49ae9b6b10e82d4965fddecbef
languageName: node
linkType: hard
@@ -11175,13 +11193,6 @@ __metadata:
languageName: node
linkType: hard
"minipass@npm:^4.0.0":
version: 4.2.8
resolution: "minipass@npm:4.2.8"
checksum: 10/e148eb6dcb85c980234cad889139ef8ddf9d5bdac534f4f0268446c8792dd4c74f4502479be48de3c1cce2f6450f6da4d0d4a86405a8a12be04c1c36b339569a
languageName: node
linkType: hard
"minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2, minipass@npm:^7.1.3":
version: 7.1.3
resolution: "minipass@npm:7.1.3"
@@ -12577,8 +12588,8 @@ __metadata:
linkType: hard
"rollup@npm:^2.43.1":
version: 2.79.2
resolution: "rollup@npm:2.79.2"
version: 2.80.0
resolution: "rollup@npm:2.80.0"
dependencies:
fsevents: "npm:~2.3.2"
dependenciesMeta:
@@ -12586,7 +12597,7 @@ __metadata:
optional: true
bin:
rollup: dist/bin/rollup
checksum: 10/095ba0a82811b1866a76d826987743278db0a87c45092656986bfff490326b66187d5f9ff0c24cf8d5682bc470aa00c36654e0044d6b6335ac0c1201b8280880
checksum: 10/1150ab0f71d59e25a0fe6c0d07e49615ada1e9deba1754073be527c48c558a019fcd31d4d927a9c172593b7dc9c7c3c6871ef07fe1e575371ee24400a7c58213
languageName: node
linkType: hard
@@ -13734,16 +13745,16 @@ __metadata:
languageName: node
linkType: hard
"tar@npm:7.5.8, tar@npm:^7.5.2":
version: 7.5.8
resolution: "tar@npm:7.5.8"
"tar@npm:*, tar@npm:7.5.9, tar@npm:^7.5.2":
version: 7.5.9
resolution: "tar@npm:7.5.9"
dependencies:
"@isaacs/fs-minipass": "npm:^4.0.0"
chownr: "npm:^3.0.0"
minipass: "npm:^7.1.2"
minizlib: "npm:^3.1.0"
yallist: "npm:^5.0.0"
checksum: 10/5fddc22e0fd03e73d5e9e922e71d8681f85443dee4f21403059a757e186ae4004abc9a709cdc7f4143d7d75758a2935f7306b3cc193123d46b6f786dd2b99c2a
checksum: 10/1213cdde9c22d6acf8809ba5d2a025212ce3517bc99c4a4c6981b7dc0489bf3b164db9c826c9517680889194c9ba57448c8ff0da35eca9a60bb7689bf0b3897d
languageName: node
linkType: hard
@@ -14201,18 +14212,18 @@ __metadata:
languageName: node
linkType: hard
"typescript-eslint@npm:8.54.0":
version: 8.54.0
resolution: "typescript-eslint@npm:8.54.0"
"typescript-eslint@npm:8.56.0":
version: 8.56.0
resolution: "typescript-eslint@npm:8.56.0"
dependencies:
"@typescript-eslint/eslint-plugin": "npm:8.54.0"
"@typescript-eslint/parser": "npm:8.54.0"
"@typescript-eslint/typescript-estree": "npm:8.54.0"
"@typescript-eslint/utils": "npm:8.54.0"
"@typescript-eslint/eslint-plugin": "npm:8.56.0"
"@typescript-eslint/parser": "npm:8.56.0"
"@typescript-eslint/typescript-estree": "npm:8.56.0"
"@typescript-eslint/utils": "npm:8.56.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/21b1a27fd44716df8d2c7bac4ebd0caef196a04375fff7919dc817066017b6b8700f1e242bd065a26ac7ce0505b7a588626099e04a28142504ed4f0aae8bffb1
checksum: 10/7c07af35e6b4eaaebad4b50c37bc6dd50f0cecebe5a4e648ef117fd43a8496f3132020061e19a2fbaf826978e91c100054e638701bf89db8f342dd1353bb5b7e
languageName: node
linkType: hard
@@ -14335,10 +14346,10 @@ __metadata:
languageName: node
linkType: hard
"undici@npm:^7.20.0":
version: 7.20.0
resolution: "undici@npm:7.20.0"
checksum: 10/09ca3e1255cf05f3c76e6dff2ae760131ea5bba57290b9b184bd94f5167939548e7ea73292c524c25eb91f5a2152623394d4c6124e222d34fcd53ef733c6b156
"undici@npm:^7.21.0":
version: 7.22.0
resolution: "undici@npm:7.22.0"
checksum: 10/a7a1813ba4b74c0d46cc8dd160386202c05699ffc487c5d882cf40e6d2435c8d6faff3b8f8675d09bd1ef0386e370675c26b59b9a8c8b3f17b9f82a42236a927
languageName: node
linkType: hard
@@ -14593,16 +14604,16 @@ __metadata:
languageName: node
linkType: hard
"vite-tsconfig-paths@npm:6.0.5":
version: 6.0.5
resolution: "vite-tsconfig-paths@npm:6.0.5"
"vite-tsconfig-paths@npm:6.1.1":
version: 6.1.1
resolution: "vite-tsconfig-paths@npm:6.1.1"
dependencies:
debug: "npm:^4.1.1"
globrex: "npm:^0.1.2"
tsconfck: "npm:^3.0.3"
peerDependencies:
vite: "*"
checksum: 10/1c3d38102ed34d057fc602c332bfd059bfedd0b378ee87b1a73eac89e20f6d81ee4bd9639557287e275cae2230f1d9225d2d7d83a2fa355a380cf77568f2cd31
checksum: 10/f752bce4f3c5707f0df7af8a20294b1f325e26f50578b82c8262d851028616ebb1a3e73ab0789c55cf3c8da8d985e843193c0bec2cb31662c567ccdf137f1fd0
languageName: node
linkType: hard