mirror of
https://github.com/home-assistant/frontend.git
synced 2026-10-13 08:14:38 +00:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14866713ba | ||
|
|
d1365b02da | ||
|
|
707dc96fb1 | ||
|
|
db1a628b57 | ||
|
|
21c059e9fc | ||
|
|
33c734ea8b |
No files matched your search
+2
-2
@@ -168,7 +168,7 @@
|
||||
"@types/leaflet.markercluster": "1.5.6",
|
||||
"@types/lodash.merge": "4.6.9",
|
||||
"@types/luxon": "3.7.6",
|
||||
"@types/node": "24.19.1",
|
||||
"@types/node": "24.19.2",
|
||||
"@types/qrcode": "1.5.6",
|
||||
"@types/sortablejs": "1.15.9",
|
||||
"@types/tar": "7.0.87",
|
||||
@@ -225,7 +225,7 @@
|
||||
"webpackbar": "7.0.0",
|
||||
"workbox-build": "7.4.1"
|
||||
},
|
||||
"packageManager": "[email protected].1",
|
||||
"packageManager": "[email protected].2",
|
||||
"volta": {
|
||||
"node": "24.21.0"
|
||||
}
|
||||
|
||||
Generated
+92
-92
@@ -7,153 +7,153 @@ importers:
|
||||
configDependencies: {}
|
||||
packageManagerDependencies:
|
||||
pnpm:
|
||||
specifier: 12.11.1
|
||||
version: 12.11.1
|
||||
specifier: 12.11.2
|
||||
version: 12.11.2
|
||||
|
||||
packages:
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
resolution: {integrity: sha512-zoVTfgk2x7/Co7jBGDiDs9dyi2YnxdIDc0+iV2z+QxXHHq39hSJemhuyAgHRiwjpa052+FreoZx0C6nZ/kj1Lw==}
|
||||
'@pnpm/[email protected].2':
|
||||
resolution: {integrity: sha512-owLw13OfPkEoY+hLBNeeG+dCFhywr7q2d/EHPz4mNL2RhKnv8gNwMARkjkiDQu234aItVwFNDeGnmQXm+ikkAg==}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
resolution: {integrity: sha512-vPO6nxrh1m18dkhf8DdqcX7ePNGDj4OWXTp6CFZOspc6NJavmMgKMsaV2ChCRyoe+thACnPdOyVic5HucYOK8Q==}
|
||||
'@pnpm/[email protected].2':
|
||||
resolution: {integrity: sha512-GYPkVebl4iokOA/4MPSpGoUVfFlsth7uqOCe1XiaiM23LAdvUOMJBObqHJijr89ZqjwHm9pO6vO45GRwJ2U2qA==}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
resolution: {integrity: sha512-MLzzUKCLgJ/yHdO+qv/Ndk+lIPhtEUfkdVE2aznJ9/5R8ipuSwKBgR66hwjU6fHANERxl05zWe3G5gGYvoFvkw==}
|
||||
'@pnpm/[email protected].2':
|
||||
resolution: {integrity: sha512-n9M4On66y8FDxUAKgMO1FXt0qihyG4e+56p3/F0uo8Bx9KplW8BwKVnXigzCJ8aNM6sVVPkOE6M9XXbeVIxnZA==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
resolution: {integrity: sha512-Rh5j7Vio5vmJR9hACJdgMrnEMIUNGcRc07o01mBdm6qtCohzbu0YyAKyV/VOKK/HQ19yls/7h1ktaY6orOX41A==}
|
||||
'@pnpm/[email protected].2':
|
||||
resolution: {integrity: sha512-oSW7n3sBT0c0cyk4kj10T2Y6Mpm+OFEiCAWozgX039tdoI6yqJF1uaFD3sw7ugLdu6VV3+uY0pa1TUZIPdxDWA==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
resolution: {integrity: sha512-y8UlliptKeCtJwQNuS5KvvX69x/um34+93kicwPMRGq4WIPA6XDEHDYJJUR8eigo/fmN59ge0o9hQL5PNHvalA==}
|
||||
'@pnpm/[email protected].2':
|
||||
resolution: {integrity: sha512-qbzc3KoDpa/Lkf8vs3jnDe6dUUbso+6eLIyssYFMiliLpYNxn8dirk11YoNnOQcdHVB50v5RFls1Yid6ttT5pg==}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
resolution: {integrity: sha512-4EmYe+LjHZMt9alrhpRd5W1oLuvWDt4wO0utSLCbrny5jOrl0PBYfLiPd2NLeIyaguV6Gtv0cLLC4gBzK1/blA==}
|
||||
'@pnpm/[email protected].2':
|
||||
resolution: {integrity: sha512-uMEYDwKRktGwANubQ+kYCwmhNwg3Ik6qK89ahsbQ5cP30R/5FVJb/T99vr9XRUBC/A7mZI6eeT92oyVas9ACyg==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
resolution: {integrity: sha512-NoRfRJ6aHpph2aYNdpe1GszTTNpfdiqquaUTzONBc2v4MyzWfQ3ZskEGrf4J8yKnk/Mr61CaNG/awMpg4FwWCw==}
|
||||
'@pnpm/[email protected].2':
|
||||
resolution: {integrity: sha512-6e8ZAQUwFXAWHHHv5FesWqf/kEK04wuitBv5f/gxzkC0DiBfaixr+f+6JQede8SF4Ji5LcMwVFJbzAtffpWxEg==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
resolution: {integrity: sha512-VFtJm5fN2SDISD0yihaITm9vWokuUAlM3FztEmNP1TrGGiMOfm3pUloekNk8coyxh0R12EILy92r/Zf7Fldvaw==}
|
||||
'@pnpm/[email protected].2':
|
||||
resolution: {integrity: sha512-Ui8A4nepkVupx2VpuFfysEluMvWK6gTB5rgVts7Ss/+8dj0RD28x3F/seNeH/zSypgC6yKdV6qsKVQh/uffTuQ==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
resolution: {integrity: sha512-dy5zX4ve0H7iDzQep/xGqaLm9bzL1QOvuulIxDOVLd3rbpxtf8P0Fu4OJ3uBjIj+xJ4k5XSLgpWMVL0/A+qFIA==}
|
||||
'@pnpm/[email protected].2':
|
||||
resolution: {integrity: sha512-i1yLdQ5bu+wjQ+srtFoJAaY2gN/+oXHFdjul4llCeoDxHE0rTIG3R41NhHfCjyFPW4+CpbBT58933CL9sMOCnA==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
resolution: {integrity: sha512-xr/oooiSgbwT3n/9Djy6gStvVllIPEUJvI+p3lpk3wvvSwsxag7URvAb3E0s38DybnzuO8w/TGWn0KCdLnMBIA==}
|
||||
'@pnpm/[email protected].2':
|
||||
resolution: {integrity: sha512-YS1/lhPjjTsN4QL50VaFZKRa3Uc/bDnhE8eKbJMFvuX06UuFS5YaiDg04APoZdfVSGqTfX27QAg7rfLMSWaqxA==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
resolution: {integrity: sha512-kWWVru2mB3iOB5zBnfXVJkF8GAPhIA0TuL8XLrdhYWUrku8XvSwieemJlJevSFMZrcZ2ZcPVXCGJeB3pmdzDMw==}
|
||||
'@pnpm/[email protected].2':
|
||||
resolution: {integrity: sha512-7eIxQR05fvJxCujUSKp5KTtehJ7QqrgqP/HD1+BcB/SaV2Be4XI1PEHiOgA1zxsiMIeMvTF6E4G4dYHrFjeXRg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
resolution: {integrity: sha512-c7kh0wtjg05in2wkmn6frs/tciuaGw8XNZUuggR88mvUvmJ81jsCP1MpM85wTFEptdeB4OnlRG0ESUZbu49q8Q==}
|
||||
'@pnpm/[email protected].2':
|
||||
resolution: {integrity: sha512-FK7QKe24AYgppJwO3mpooYptduuvIGqKJPi5ac7MdnWkjUy46JBfYhCW0grnkWKC8YOf0x+vGviuQqJEj3SpQQ==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
resolution: {integrity: sha512-UqURM0VnIONCwFCp3Hz9o6X6gwcyTDvetbYJjAcsCwr4Rega2IXB0s/E+mQUpOUG5zl48Yi7xhLJI1U78OM+FQ==}
|
||||
'@pnpm/[email protected].2':
|
||||
resolution: {integrity: sha512-PCTjVDdJb6VvraZxrEeFv6EqEA0hhIsAySx28e95J2Tqpi88hLpgOXgNqV0vpxq9A+b8GLQfyk/D+1UlJPv/YQ==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
resolution: {integrity: sha512-aZaGoOswHuooaeCQ0+WS75FmxE9lg1zTjffHnY1gQN9Na/DX1oQ0MsyseadbzZoFF/i6XtnMvE7m5vpTyTRJ8g==}
|
||||
'@pnpm/[email protected].2':
|
||||
resolution: {integrity: sha512-8sgX86OvRjJ3yZs/TvGR2L31wH9ScPZ+f+geKI3ikuB0RRiK3i4LlTRO8OXN6pdRqj8epMde4312MCjj7JsJdA==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
[email protected].1:
|
||||
resolution: {integrity: sha512-Ww4yiCOafqQVXSCU9UcGbhIqE3azzXhaaAyREuEWkT5kqq7PIN2DzDQDD6ZwE/XFUR+c9DDdg4QYi0xguKKtHg==}
|
||||
[email protected].2:
|
||||
resolution: {integrity: sha512-JaUWUqGWeFr18ovabf6NxYLr76SsHRgCIc4TkCMcX7dmN4IupLbkYzznSs3LCnf3Ja8RutOBd/3t2u6BipVcuQ==}
|
||||
engines: {node: '>=18.*'}
|
||||
hasBin: true
|
||||
|
||||
snapshots:
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
'@pnpm/[email protected].2':
|
||||
optional: true
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
'@pnpm/[email protected].2':
|
||||
optional: true
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
'@pnpm/[email protected].2':
|
||||
optional: true
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
'@pnpm/[email protected].2':
|
||||
optional: true
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
'@pnpm/[email protected].2':
|
||||
optional: true
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
'@pnpm/[email protected].2':
|
||||
optional: true
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
'@pnpm/[email protected].2':
|
||||
optional: true
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
'@pnpm/[email protected].2':
|
||||
optional: true
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
'@pnpm/[email protected].2':
|
||||
optional: true
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
'@pnpm/[email protected].2':
|
||||
optional: true
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
'@pnpm/[email protected].2':
|
||||
optional: true
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
'@pnpm/[email protected].2':
|
||||
optional: true
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
'@pnpm/[email protected].2':
|
||||
optional: true
|
||||
|
||||
'@pnpm/[email protected].1':
|
||||
'@pnpm/[email protected].2':
|
||||
optional: true
|
||||
|
||||
[email protected].1:
|
||||
[email protected].2:
|
||||
optionalDependencies:
|
||||
'@pnpm/exe.android-arm64': 12.11.1
|
||||
'@pnpm/exe.android-x64': 12.11.1
|
||||
'@pnpm/exe.darwin-arm64': 12.11.1
|
||||
'@pnpm/exe.darwin-x64': 12.11.1
|
||||
'@pnpm/exe.freebsd-x64': 12.11.1
|
||||
'@pnpm/exe.linux-arm64': 12.11.1
|
||||
'@pnpm/exe.linux-arm64-musl': 12.11.1
|
||||
'@pnpm/exe.linux-ppc64': 12.11.1
|
||||
'@pnpm/exe.linux-riscv64': 12.11.1
|
||||
'@pnpm/exe.linux-s390x': 12.11.1
|
||||
'@pnpm/exe.linux-x64': 12.11.1
|
||||
'@pnpm/exe.linux-x64-musl': 12.11.1
|
||||
'@pnpm/exe.win32-arm64': 12.11.1
|
||||
'@pnpm/exe.win32-x64': 12.11.1
|
||||
'@pnpm/exe.android-arm64': 12.11.2
|
||||
'@pnpm/exe.android-x64': 12.11.2
|
||||
'@pnpm/exe.darwin-arm64': 12.11.2
|
||||
'@pnpm/exe.darwin-x64': 12.11.2
|
||||
'@pnpm/exe.freebsd-x64': 12.11.2
|
||||
'@pnpm/exe.linux-arm64': 12.11.2
|
||||
'@pnpm/exe.linux-arm64-musl': 12.11.2
|
||||
'@pnpm/exe.linux-ppc64': 12.11.2
|
||||
'@pnpm/exe.linux-riscv64': 12.11.2
|
||||
'@pnpm/exe.linux-s390x': 12.11.2
|
||||
'@pnpm/exe.linux-x64': 12.11.2
|
||||
'@pnpm/exe.linux-x64-musl': 12.11.2
|
||||
'@pnpm/exe.win32-arm64': 12.11.2
|
||||
'@pnpm/exe.win32-x64': 12.11.2
|
||||
|
||||
---
|
||||
lockfileVersion: '9.0'
|
||||
@@ -267,7 +267,7 @@ importers:
|
||||
version: 6.1.21(@fullcalendar/[email protected])
|
||||
'@home-assistant/webawesome':
|
||||
specifier: 3.14.0-ha.0
|
||||
version: 3.14.0-ha.0(@floating-ui/[email protected])(@types/[email protected].1)
|
||||
version: 3.14.0-ha.0(@floating-ui/[email protected])(@types/[email protected].2)
|
||||
'@lezer/common':
|
||||
specifier: 1.5.3
|
||||
version: 1.5.3
|
||||
@@ -561,8 +561,8 @@ importers:
|
||||
specifier: 3.7.6
|
||||
version: 3.7.6
|
||||
'@types/node':
|
||||
specifier: 24.19.1
|
||||
version: 24.19.1
|
||||
specifier: 24.19.2
|
||||
version: 24.19.2
|
||||
'@types/qrcode':
|
||||
specifier: 1.5.6
|
||||
version: 1.5.6
|
||||
@@ -712,13 +712,13 @@ importers:
|
||||
version: 8.71.1([email protected]([email protected]))([email protected])([email protected])
|
||||
vite:
|
||||
specifier: 8.3.4
|
||||
version: 8.3.4(@types/[email protected].1)([email protected])([email protected])
|
||||
version: 8.3.4(@types/[email protected].2)([email protected])([email protected])
|
||||
vite-tsconfig-paths:
|
||||
specifier: 6.1.1
|
||||
version: 6.1.1([email protected])([email protected])([email protected](@types/[email protected].1)([email protected])([email protected]))
|
||||
version: 6.1.1([email protected])([email protected])([email protected](@types/[email protected].2)([email protected])([email protected]))
|
||||
vitest:
|
||||
specifier: 5.0.3
|
||||
version: 5.0.3(@types/[email protected].1)(@vitest/[email protected])([email protected])([email protected](@types/[email protected].1)([email protected])([email protected]))
|
||||
version: 5.0.3(@types/[email protected].2)(@vitest/[email protected])([email protected])([email protected](@types/[email protected].2)([email protected])([email protected]))
|
||||
webpack-stats-plugin:
|
||||
specifier: 1.1.3
|
||||
version: 1.1.3
|
||||
@@ -3366,8 +3366,8 @@ packages:
|
||||
'@types/[email protected]':
|
||||
resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==}
|
||||
|
||||
'@types/[email protected].1':
|
||||
resolution: {integrity: sha512-aS3/DG0oM05K0RIXXP+hKjinGG5IgSSVGzswZxW3O0sS3pH4/fycXundUC9XsszgKCk4gHXylTEK6hyFxVxnoQ==}
|
||||
'@types/[email protected].2':
|
||||
resolution: {integrity: sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ==}
|
||||
|
||||
'@types/[email protected]':
|
||||
resolution: {integrity: sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==}
|
||||
@@ -9499,12 +9499,12 @@ snapshots:
|
||||
dependencies:
|
||||
is-negated-glob: 1.0.0
|
||||
|
||||
'@home-assistant/[email protected](@floating-ui/[email protected])(@types/[email protected].1)':
|
||||
'@home-assistant/[email protected](@floating-ui/[email protected])(@types/[email protected].2)':
|
||||
dependencies:
|
||||
'@ctrl/tinycolor': 4.1.0
|
||||
'@floating-ui/dom': 1.8.0
|
||||
'@konnorr/qr-creator': 1.1.0
|
||||
'@lit-labs/ssr': 4.1.0(@types/[email protected].1)
|
||||
'@lit-labs/ssr': 4.1.0(@types/[email protected].2)
|
||||
'@lit-labs/ssr-client': 1.1.8
|
||||
'@lit/context': 1.1.6
|
||||
'@lit/react': 1.0.8
|
||||
@@ -9740,7 +9740,7 @@ snapshots:
|
||||
|
||||
'@lit-labs/[email protected]': {}
|
||||
|
||||
'@lit-labs/[email protected](@types/[email protected].1)':
|
||||
'@lit-labs/[email protected](@types/[email protected].2)':
|
||||
dependencies:
|
||||
'@lit-labs/ssr-client': 1.1.8
|
||||
'@lit-labs/ssr-dom-shim': 1.6.0
|
||||
@@ -9753,7 +9753,7 @@ snapshots:
|
||||
node-fetch: 3.3.2
|
||||
parse5: 7.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 24.19.1
|
||||
'@types/node': 24.19.2
|
||||
|
||||
'@lit-labs/[email protected]':
|
||||
dependencies:
|
||||
@@ -10976,11 +10976,11 @@ snapshots:
|
||||
|
||||
'@types/[email protected]':
|
||||
dependencies:
|
||||
'@types/node': 24.19.1
|
||||
'@types/node': 24.19.2
|
||||
|
||||
'@types/[email protected]':
|
||||
dependencies:
|
||||
'@types/node': 24.19.1
|
||||
'@types/node': 24.19.2
|
||||
|
||||
'@types/[email protected]': {}
|
||||
|
||||
@@ -11038,7 +11038,7 @@ snapshots:
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
|
||||
'@types/[email protected].1':
|
||||
'@types/[email protected].2':
|
||||
dependencies:
|
||||
undici-types: 7.24.6
|
||||
|
||||
@@ -11046,11 +11046,11 @@ snapshots:
|
||||
|
||||
'@types/[email protected]':
|
||||
dependencies:
|
||||
'@types/node': 24.19.1
|
||||
'@types/node': 24.19.2
|
||||
|
||||
'@types/[email protected]':
|
||||
dependencies:
|
||||
'@types/node': 24.19.1
|
||||
'@types/node': 24.19.2
|
||||
|
||||
'@types/[email protected]': {}
|
||||
|
||||
@@ -11068,7 +11068,7 @@ snapshots:
|
||||
|
||||
'@types/[email protected]':
|
||||
dependencies:
|
||||
'@types/node': 24.19.1
|
||||
'@types/node': 24.19.2
|
||||
|
||||
'@typescript-eslint/[email protected](@typescript-eslint/[email protected]([email protected]([email protected]))([email protected])([email protected]))([email protected]([email protected]))([email protected])([email protected])':
|
||||
dependencies:
|
||||
@@ -11362,7 +11362,7 @@ snapshots:
|
||||
obug: 2.1.4
|
||||
std-env: 4.2.0
|
||||
tinyrainbow: 3.1.1
|
||||
vitest: 5.0.3(@types/[email protected].1)(@vitest/[email protected])([email protected])([email protected](@types/[email protected].1)([email protected])([email protected]))
|
||||
vitest: 5.0.3(@types/[email protected].2)(@vitest/[email protected])([email protected])([email protected](@types/[email protected].2)([email protected])([email protected]))
|
||||
|
||||
'@vitest/[email protected]': {}
|
||||
|
||||
@@ -11370,14 +11370,14 @@ snapshots:
|
||||
dependencies:
|
||||
'@vitest/istanbul-lib-coverage': 1.0.1
|
||||
|
||||
'@vitest/[email protected]([email protected](@types/[email protected].1)([email protected])([email protected]))':
|
||||
'@vitest/[email protected]([email protected](@types/[email protected].2)([email protected])([email protected]))':
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
'@vitest/spy': 5.0.3
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 1.2.3
|
||||
optionalDependencies:
|
||||
vite: 8.3.4(@types/[email protected].1)([email protected])([email protected])
|
||||
vite: 8.3.4(@types/[email protected].2)([email protected])([email protected])
|
||||
|
||||
'@vitest/[email protected]': {}
|
||||
|
||||
@@ -12237,7 +12237,7 @@ snapshots:
|
||||
[email protected]([email protected]):
|
||||
dependencies:
|
||||
'@types/cors': 2.8.19
|
||||
'@types/node': 24.19.1
|
||||
'@types/node': 24.19.2
|
||||
'@types/ws': 8.18.1
|
||||
accepts: 1.3.8
|
||||
base64id: 2.0.0
|
||||
@@ -13330,7 +13330,7 @@ snapshots:
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/node': 24.19.1
|
||||
'@types/node': 24.19.2
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 8.1.1
|
||||
|
||||
@@ -15416,17 +15416,17 @@ snapshots:
|
||||
- bare-abort-controller
|
||||
- react-native-b4a
|
||||
|
||||
[email protected]([email protected])([email protected])([email protected](@types/[email protected].1)([email protected])([email protected])):
|
||||
[email protected]([email protected])([email protected])([email protected](@types/[email protected].2)([email protected])([email protected])):
|
||||
dependencies:
|
||||
debug: 4.4.3([email protected])
|
||||
globrex: 0.1.2
|
||||
tsconfck: 3.1.6([email protected])
|
||||
vite: 8.3.4(@types/[email protected].1)([email protected])([email protected])
|
||||
vite: 8.3.4(@types/[email protected].2)([email protected])([email protected])
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
[email protected](@types/[email protected].1)([email protected])([email protected]):
|
||||
[email protected](@types/[email protected].2)([email protected])([email protected]):
|
||||
dependencies:
|
||||
lightningcss: 1.33.0
|
||||
picomatch: 4.0.7
|
||||
@@ -15434,15 +15434,15 @@ snapshots:
|
||||
rolldown: 1.2.13
|
||||
tinyglobby: 0.2.17
|
||||
optionalDependencies:
|
||||
'@types/node': 24.19.1
|
||||
'@types/node': 24.19.2
|
||||
fsevents: 2.3.3
|
||||
terser: 5.50.0
|
||||
yaml: 2.9.1
|
||||
|
||||
[email protected](@types/[email protected].1)(@vitest/[email protected])([email protected])([email protected](@types/[email protected].1)([email protected])([email protected])):
|
||||
[email protected](@types/[email protected].2)(@vitest/[email protected])([email protected])([email protected](@types/[email protected].2)([email protected])([email protected])):
|
||||
dependencies:
|
||||
'@types/chai': 5.2.3
|
||||
'@vitest/mocker': 5.0.3([email protected](@types/[email protected].1)([email protected])([email protected]))
|
||||
'@vitest/mocker': 5.0.3([email protected](@types/[email protected].2)([email protected])([email protected]))
|
||||
chai: 6.2.2
|
||||
es-module-lexer: 2.3.2
|
||||
expect-type: 1.4.0
|
||||
@@ -15453,10 +15453,10 @@ snapshots:
|
||||
tinybench: 6.2.0
|
||||
tinyexec: 1.3.1
|
||||
tinyglobby: 0.2.17
|
||||
vite: 8.3.4(@types/[email protected].1)([email protected])([email protected])
|
||||
vite: 8.3.4(@types/[email protected].2)([email protected])([email protected])
|
||||
why-is-node-running: 3.2.1
|
||||
optionalDependencies:
|
||||
'@types/node': 24.19.1
|
||||
'@types/node': 24.19.2
|
||||
'@vitest/coverage-v8': 5.0.3([email protected])
|
||||
jsdom: 30.1.2
|
||||
transitivePeerDependencies:
|
||||
|
||||
@@ -66,15 +66,15 @@ export class DialogDataTableSettings extends LitElement {
|
||||
}
|
||||
|
||||
private _preserveLastFixed(columnOrder) {
|
||||
let strippedColumnOrder;
|
||||
const lastFixedCount = this._lastFixedCount();
|
||||
if (lastFixedCount && columnOrder) {
|
||||
strippedColumnOrder = [...columnOrder];
|
||||
strippedColumnOrder.splice(
|
||||
columnOrder.length - lastFixedCount,
|
||||
lastFixedCount
|
||||
);
|
||||
if (!lastFixedCount || !columnOrder) {
|
||||
return columnOrder;
|
||||
}
|
||||
const strippedColumnOrder = [...columnOrder];
|
||||
strippedColumnOrder.splice(
|
||||
columnOrder.length - lastFixedCount,
|
||||
lastFixedCount
|
||||
);
|
||||
return strippedColumnOrder;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import type {
|
||||
LitVirtualizer,
|
||||
VisibilityChangedEvent,
|
||||
} from "@lit-labs/virtualizer";
|
||||
import type { ContextType } from "@lit/context";
|
||||
import { mdiArrowDown, mdiArrowUp, mdiChevronUp } from "@mdi/js";
|
||||
import deepClone from "deep-clone-simple";
|
||||
@@ -191,7 +195,29 @@ export class HaDataTable extends LitElement {
|
||||
|
||||
@query(".mdc-data-table__header-row") private _headerRow?: HTMLDivElement;
|
||||
|
||||
@query("lit-virtualizer") private _scroller?: HTMLElement;
|
||||
@query("lit-virtualizer")
|
||||
private _scroller?: LitVirtualizer<DataTableRowData>;
|
||||
|
||||
@query(".sticky-group-header") private _stickyGroupHeader?: HTMLElement;
|
||||
|
||||
@query(".sticky-group-header .group-header")
|
||||
private _stickyGroupHeaderCell?: HTMLElement;
|
||||
|
||||
// Group pinned to the top of the scroller while its own header is scrolled out of view
|
||||
@state() private _stickyGroup?: string;
|
||||
|
||||
// Position and width of the sticky group header, applied directly on scroll
|
||||
// to avoid re-rendering the table on every frame
|
||||
private _stickyGroupPosition = { top: 0, width: 0, scrollLeft: 0 };
|
||||
|
||||
private _stickyGroupFrame?: number;
|
||||
|
||||
// Resizing doesn't scroll, but changes the width of the sticky group header
|
||||
private _resizeObserver = new ResizeObserver(() =>
|
||||
this._scheduleStickyGroupUpdate()
|
||||
);
|
||||
|
||||
private _firstVisibleIndex = 0;
|
||||
|
||||
@state() private _collapsedGroups: string[] = [];
|
||||
|
||||
@@ -237,6 +263,7 @@ export class HaDataTable extends LitElement {
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._resizeObserver.observe(this);
|
||||
if (this._filteredData?.length) {
|
||||
// Force update of location of rows
|
||||
this._filteredData = [...this._filteredData];
|
||||
@@ -247,7 +274,21 @@ export class HaDataTable extends LitElement {
|
||||
this.updateComplete.then(() => this._calcTableHeight());
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._resizeObserver.disconnect();
|
||||
if (this._stickyGroupFrame !== undefined) {
|
||||
cancelAnimationFrame(this._stickyGroupFrame);
|
||||
this._stickyGroupFrame = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues<this>) {
|
||||
// The sticky group header may have just been rendered, and the items may
|
||||
// have changed (filter, sort, collapse) without the visible range changing
|
||||
this._positionStickyGroupHeader();
|
||||
this._scheduleStickyGroupUpdate();
|
||||
|
||||
if (!this._headerRow) {
|
||||
return;
|
||||
}
|
||||
@@ -343,6 +384,7 @@ export class HaDataTable extends LitElement {
|
||||
fireEvent(this, "collapsed-changed", { value: this._collapsedGroups });
|
||||
} else if (properties.has("groupColumn")) {
|
||||
this._collapsedGroups = [];
|
||||
this._stickyGroup = undefined;
|
||||
this._lastSelectedRowId = null;
|
||||
fireEvent(this, "collapsed-changed", { value: this._collapsedGroups });
|
||||
}
|
||||
@@ -373,6 +415,12 @@ export class HaDataTable extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _memRenderRow = memoizeOne(
|
||||
(columns: DataTableColumnContainer, narrow: boolean) =>
|
||||
(row: DataTableRowData, index: number) =>
|
||||
this._renderRow(columns, narrow, row, index)
|
||||
);
|
||||
|
||||
private _sortedColumns = memoizeOne(
|
||||
(columns: DataTableColumnContainer, columnOrder?: string[]) => {
|
||||
if (!columnOrder || !columnOrder.length) {
|
||||
@@ -408,8 +456,7 @@ export class HaDataTable extends LitElement {
|
||||
protected render() {
|
||||
const columns = this._sortedColumns(this.columns, this.columnOrder);
|
||||
|
||||
const renderRow = (row: DataTableRowData, index: number) =>
|
||||
this._renderRow(columns, this.narrow, row, index);
|
||||
const renderRow = this._memRenderRow(columns, this.narrow);
|
||||
|
||||
const filteredDataLength = this._filteredData?.length || 0;
|
||||
|
||||
@@ -655,9 +702,12 @@ export class HaDataTable extends LitElement {
|
||||
: html`
|
||||
<lit-virtualizer
|
||||
scroller
|
||||
class="mdc-data-table__content scroller ha-scrollbar"
|
||||
class="mdc-data-table__content scroller ha-scrollbar ${classMap(
|
||||
{ grouped: Boolean(this.groupColumn) && !this.autoHeight }
|
||||
)}"
|
||||
tabindex=${ifDefined(!this.autoHeight ? "0" : undefined)}
|
||||
@scroll=${this._saveScrollPos}
|
||||
@visibilityChanged=${this._handleVisibilityChanged}
|
||||
.items=${this._groupData(
|
||||
this._filteredData,
|
||||
this._i18n?.localize,
|
||||
@@ -672,6 +722,25 @@ export class HaDataTable extends LitElement {
|
||||
.keyFunction=${this._keyFunction}
|
||||
.renderItem=${renderRow}
|
||||
></lit-virtualizer>
|
||||
${
|
||||
this.groupColumn &&
|
||||
!this.autoHeight &&
|
||||
this._stickyGroup !== undefined
|
||||
? html`<div
|
||||
class="mdc-data-table__row sticky-group-header"
|
||||
aria-hidden="true"
|
||||
.group=${this._stickyGroup}
|
||||
@click=${this._collapseStickyGroup}
|
||||
>
|
||||
${this._renderGroupHeader(
|
||||
this._stickyGroup,
|
||||
this._collapsedGroups.includes(this._stickyGroup),
|
||||
this._i18n?.localize,
|
||||
true
|
||||
)}
|
||||
</div>`
|
||||
: nothing
|
||||
}
|
||||
`
|
||||
}
|
||||
</div>
|
||||
@@ -692,7 +761,14 @@ export class HaDataTable extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
if (row.append) {
|
||||
return html`<div class="mdc-data-table__row">${row.content}</div>`;
|
||||
return html`<div
|
||||
class="mdc-data-table__row ${classMap({
|
||||
"group-header-row": row.groupHeader !== undefined,
|
||||
})}"
|
||||
.group=${row.groupHeader}
|
||||
>
|
||||
${row.content}
|
||||
</div>`;
|
||||
}
|
||||
if (row.empty) {
|
||||
return html`<div class="mdc-data-table__row empty-row"></div>`;
|
||||
@@ -800,7 +876,13 @@ export class HaDataTable extends LitElement {
|
||||
key: string,
|
||||
column: DataTableColumnData
|
||||
): boolean {
|
||||
if (column.main || column.showNarrow) {
|
||||
// The grouped value is already shown in the sticky group header, which
|
||||
// isn't available for auto-height tables
|
||||
if (
|
||||
column.main ||
|
||||
column.showNarrow ||
|
||||
(!this.autoHeight && key === this.groupColumn)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return this._isColumnVisible(key, column);
|
||||
@@ -948,33 +1030,15 @@ export class HaDataTable extends LitElement {
|
||||
|
||||
const groupedItems: DataTableRowData[] = [];
|
||||
sortedEntries.forEach(([groupName, rows]) => {
|
||||
const collapsed = collapsedGroups.includes(groupName);
|
||||
groupedItems.push({
|
||||
append: true,
|
||||
selectable: false,
|
||||
content: html`<div
|
||||
class="mdc-data-table__cell group-header"
|
||||
role="cell"
|
||||
.group=${groupName}
|
||||
@click=${this._collapseGroup}
|
||||
>
|
||||
<ha-icon-button
|
||||
.path=${mdiChevronUp}
|
||||
.label=${
|
||||
localize?.(
|
||||
`ui.components.data-table.${collapsed ? "expand" : "collapse"}`
|
||||
) || (collapsed ? "Expand" : "Collapse")
|
||||
}
|
||||
class=${collapsed ? "collapsed" : ""}
|
||||
>
|
||||
</ha-icon-button>
|
||||
${
|
||||
groupName === UNDEFINED_GROUP_KEY
|
||||
? localize?.("ui.components.data-table.ungrouped") ||
|
||||
"Ungrouped"
|
||||
: groupName || ""
|
||||
}
|
||||
</div>`,
|
||||
groupHeader: groupName,
|
||||
content: this._renderGroupHeader(
|
||||
groupName,
|
||||
collapsedGroups.includes(groupName),
|
||||
localize
|
||||
),
|
||||
});
|
||||
if (!collapsedGroups.includes(groupName)) {
|
||||
groupedItems.push(...rows);
|
||||
@@ -995,6 +1059,40 @@ export class HaDataTable extends LitElement {
|
||||
}
|
||||
);
|
||||
|
||||
private _renderGroupHeader(
|
||||
groupName: string,
|
||||
collapsed: boolean,
|
||||
localize: LocalizeFunc | undefined,
|
||||
sticky = false
|
||||
) {
|
||||
// The sticky copy is hidden from assistive technology, so its button is
|
||||
// made inert. It handles clicks on its whole row instead, as its cell
|
||||
// shifts with horizontal scrolling.
|
||||
return html`<div
|
||||
class="mdc-data-table__cell group-header"
|
||||
role="cell"
|
||||
.group=${groupName}
|
||||
@click=${sticky ? nothing : this._collapseGroup}
|
||||
>
|
||||
<ha-icon-button
|
||||
.path=${mdiChevronUp}
|
||||
.label=${
|
||||
localize?.(
|
||||
`ui.components.data-table.${collapsed ? "expand" : "collapse"}`
|
||||
) || (collapsed ? "Expand" : "Collapse")
|
||||
}
|
||||
class=${collapsed ? "collapsed" : ""}
|
||||
?inert=${sticky}
|
||||
>
|
||||
</ha-icon-button>
|
||||
${
|
||||
groupName === UNDEFINED_GROUP_KEY
|
||||
? localize?.("ui.components.data-table.ungrouped") || "Ungrouped"
|
||||
: groupName || ""
|
||||
}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private _memFilterData = memoizeOne(
|
||||
(
|
||||
data: DataTableRowData[],
|
||||
@@ -1197,6 +1295,101 @@ export class HaDataTable extends LitElement {
|
||||
if (this._headerRow) {
|
||||
this._headerRow.scrollLeft = e.target.scrollLeft;
|
||||
}
|
||||
|
||||
this._scheduleStickyGroupUpdate();
|
||||
}
|
||||
|
||||
private _handleVisibilityChanged(ev: VisibilityChangedEvent) {
|
||||
this._firstVisibleIndex = ev.first;
|
||||
// Fired before the virtualizer positions its children, so measure later
|
||||
this._scheduleStickyGroupUpdate();
|
||||
}
|
||||
|
||||
private _scheduleStickyGroupUpdate() {
|
||||
if (!this.groupColumn) {
|
||||
this._stickyGroup = undefined;
|
||||
return;
|
||||
}
|
||||
if (this._stickyGroupFrame !== undefined) {
|
||||
return;
|
||||
}
|
||||
this._stickyGroupFrame = requestAnimationFrame(() => {
|
||||
this._stickyGroupFrame = undefined;
|
||||
this._updateStickyGroup();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The virtualizer positions rows absolutely, so `position: sticky` can't be
|
||||
* used. Instead, an overlay copy of the current group header is shown at the
|
||||
* top of the scroller, and the next group header slides under it. The
|
||||
* overlay doesn't move with the scroll, as browsers scroll off the main
|
||||
* thread and it would lag behind the rows.
|
||||
*/
|
||||
private _updateStickyGroup() {
|
||||
const scroller = this._scroller;
|
||||
if (
|
||||
!scroller ||
|
||||
!this.groupColumn ||
|
||||
this.autoHeight ||
|
||||
scroller.scrollTop <= 0
|
||||
) {
|
||||
this._stickyGroup = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollerTop = scroller.getBoundingClientRect().top;
|
||||
let group: string | undefined;
|
||||
let currentTop = -Infinity;
|
||||
|
||||
scroller
|
||||
.querySelectorAll<HTMLElement & { group: string }>(".group-header-row")
|
||||
.forEach((row) => {
|
||||
const top = row.getBoundingClientRect().top - scrollerTop;
|
||||
// Rows can sit at fractional positions, so a header less than a
|
||||
// pixel below the top counts as reached
|
||||
if (top < 1 && top > currentTop) {
|
||||
currentTop = top;
|
||||
group = row.group;
|
||||
}
|
||||
});
|
||||
|
||||
if (group === undefined) {
|
||||
// The current group header is not rendered, find it from the first visible row
|
||||
const items = scroller.items;
|
||||
for (
|
||||
let i = Math.min(this._firstVisibleIndex, items.length - 1);
|
||||
i >= 0;
|
||||
i--
|
||||
) {
|
||||
if (items[i]?.append && items[i].groupHeader !== undefined) {
|
||||
group = items[i].groupHeader;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._stickyGroup = group;
|
||||
this._stickyGroupPosition = {
|
||||
top: scroller.offsetTop,
|
||||
width: scroller.clientWidth,
|
||||
scrollLeft: scroller.scrollLeft,
|
||||
};
|
||||
this._positionStickyGroupHeader();
|
||||
}
|
||||
|
||||
private _positionStickyGroupHeader() {
|
||||
const header = this._stickyGroupHeader;
|
||||
if (!header) {
|
||||
return;
|
||||
}
|
||||
const { top, width, scrollLeft } = this._stickyGroupPosition;
|
||||
header.style.width = `${width}px`;
|
||||
header.style.transform = `translateY(${top}px)`;
|
||||
// Follow the horizontal scroll of the group header rows it covers
|
||||
if (this._stickyGroupHeaderCell) {
|
||||
this._stickyGroupHeaderCell.style.transform = `translateX(${-scrollLeft}px)`;
|
||||
}
|
||||
}
|
||||
|
||||
@eventOptions({ passive: true })
|
||||
@@ -1208,6 +1401,40 @@ export class HaDataTable extends LitElement {
|
||||
this._scroller.scrollLeft = e.target.scrollLeft;
|
||||
}
|
||||
|
||||
private _collapseStickyGroup = async (
|
||||
ev: HASSDomCurrentTargetEvent<HTMLElement & { group: string }>
|
||||
) => {
|
||||
const groupName = ev.currentTarget.group;
|
||||
this._collapseGroup(ev);
|
||||
await this.updateComplete;
|
||||
const scroller = this._scroller;
|
||||
if (!scroller) {
|
||||
return;
|
||||
}
|
||||
// Keep the toggled group in view, its rows may have been above the viewport.
|
||||
// Scroll a rendered header to the very top ourselves, as scrollIntoView
|
||||
// would leave room for the sticky header (scroll-padding-top).
|
||||
const row = Array.from(
|
||||
scroller.querySelectorAll<HTMLElement & { group: string }>(
|
||||
".group-header-row"
|
||||
)
|
||||
).find((headerRow) => headerRow.group === groupName);
|
||||
if (row) {
|
||||
scroller.scrollTop +=
|
||||
row.getBoundingClientRect().top - scroller.getBoundingClientRect().top;
|
||||
return;
|
||||
}
|
||||
const index = scroller.items.findIndex(
|
||||
(item) => item?.append && item.groupHeader === groupName
|
||||
);
|
||||
if (index !== -1) {
|
||||
// The header isn't rendered, but element() still returns a proxy for
|
||||
// any index in the items: its scrollIntoView() pins that index in the
|
||||
// virtualizer's layout, which renders it and aligns it to the top
|
||||
scroller.element(index)?.scrollIntoView({ block: "start" });
|
||||
}
|
||||
};
|
||||
|
||||
private _collapseGroup = (
|
||||
ev: HASSDomCurrentTargetEvent<HTMLElement & { group: string }>
|
||||
) => {
|
||||
@@ -1562,6 +1789,28 @@ export class HaDataTable extends LitElement {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.sticky-group-header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
z-index: 1;
|
||||
/* Match the top border of the group header rows it covers */
|
||||
border-top: 1px solid transparent;
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
background-color: var(--primary-background-color);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.sticky-group-header .group-header {
|
||||
/* Don't paint over the bottom divider */
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* Keep focused rows clear of the sticky group header */
|
||||
lit-virtualizer.grouped {
|
||||
scroll-padding-top: var(--data-table-row-height, 52px);
|
||||
}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -91,6 +91,8 @@ export class HaDateTimeSelector extends LitElement {
|
||||
below for its hint while ha-time-input does not. */
|
||||
align-items: flex-start;
|
||||
flex-direction: row;
|
||||
/* Narrow screens have no room for both fields on one row. */
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
ha-date-input {
|
||||
|
||||
@@ -261,19 +261,13 @@ export const updateStatisticsMetadata = (
|
||||
unit_class,
|
||||
});
|
||||
|
||||
// Without a range, the statistics are removed completely.
|
||||
export const clearStatistics = (
|
||||
hass: Pick<HomeAssistant, "callWS">,
|
||||
statistic_ids: string[],
|
||||
range?: { start: Date; end: Date }
|
||||
statistic_ids: string[]
|
||||
) =>
|
||||
hass.callWS<undefined>({
|
||||
type: "recorder/clear_statistics",
|
||||
statistic_ids,
|
||||
...(range && {
|
||||
start_time: range.start.toISOString(),
|
||||
end_time: range.end.toISOString(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const calculateStatisticSumGrowth = (
|
||||
|
||||
@@ -339,7 +339,7 @@ class ErrorLogCard extends LitElement {
|
||||
streaming && this._boot === 0 && !this._error
|
||||
? html`<div class="live-indicator">
|
||||
<ha-svg-icon path=${mdiCircle}></ha-svg-icon>
|
||||
Live
|
||||
${localize("ui.panel.config.logs.live")}
|
||||
</div>`
|
||||
: nothing
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { formatDateTime } from "../../../../common/datetime/format_date_time";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { formatNumber } from "../../../../common/number/format_number";
|
||||
import "../../../../components/ha-spinner";
|
||||
import "../../../../components/ha-dialog-footer";
|
||||
import "../../../../components/ha-dialog";
|
||||
@@ -66,8 +67,6 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends DirtyStateProvid
|
||||
|
||||
@state() private _chosenStat?: StatisticValue;
|
||||
|
||||
private _origAmount?: number;
|
||||
|
||||
@state() private _amount?: number;
|
||||
|
||||
private _showingOutliers = false;
|
||||
@@ -125,7 +124,6 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends DirtyStateProvid
|
||||
this._moment = undefined;
|
||||
this._stats5min = undefined;
|
||||
this._statsHour = undefined;
|
||||
this._origAmount = undefined;
|
||||
this._amount = undefined;
|
||||
this._chosenStat = undefined;
|
||||
this._initDirtyTracking({ type: "deep" }, { amount: undefined });
|
||||
@@ -202,9 +200,13 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends DirtyStateProvid
|
||||
if (changedProps.size !== 1 || !changedProps.has("hass")) {
|
||||
return true;
|
||||
}
|
||||
// We only respond to hass changes if the translations changed
|
||||
// We only respond to hass changes if the translations or locale changed
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
return !oldHass || oldHass.localize !== this.hass.localize;
|
||||
return (
|
||||
!oldHass ||
|
||||
oldHass.localize !== this.hass.localize ||
|
||||
oldHass.locale !== this.hass.locale
|
||||
);
|
||||
}
|
||||
|
||||
private _renderPickStatistic() {
|
||||
@@ -228,8 +230,7 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends DirtyStateProvid
|
||||
);
|
||||
const rows: TemplateResult[] = [];
|
||||
for (const stat of data) {
|
||||
const multiple = 10 ** this._precision;
|
||||
const growth = Math.round(stat.change! * multiple) / multiple;
|
||||
const digits = this._fractionDigits(stat.change);
|
||||
rows.push(html`
|
||||
<ha-list-item
|
||||
twoline
|
||||
@@ -237,7 +238,13 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends DirtyStateProvid
|
||||
.stat=${stat}
|
||||
@click=${this._setChosenStatistic}
|
||||
>
|
||||
<span>${growth} ${unit}</span>
|
||||
<span
|
||||
>${formatNumber(stat.change!, this.hass.locale, {
|
||||
minimumFractionDigits: digits,
|
||||
maximumFractionDigits: digits,
|
||||
})}
|
||||
${unit}</span
|
||||
>
|
||||
<span slot="secondary">
|
||||
${formatDateTime(
|
||||
new Date(stat.start),
|
||||
@@ -283,15 +290,22 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends DirtyStateProvid
|
||||
this._initDirtyTracking({ type: "deep" }, { amount: undefined });
|
||||
}
|
||||
|
||||
// Small changes get more digits, so they do not round to zero.
|
||||
private _fractionDigits(value: number | null | undefined): number {
|
||||
if (!value || Math.abs(value) >= 10 ** -this._precision) {
|
||||
return this._precision;
|
||||
}
|
||||
return Math.min(Math.ceil(-Math.log10(Math.abs(value))) + 1, 20);
|
||||
}
|
||||
|
||||
private _setChosenStatistic(ev) {
|
||||
const stat = ev.currentTarget.stat;
|
||||
const multiple = 10 ** this._precision;
|
||||
const multiple = 10 ** this._fractionDigits(stat.change);
|
||||
const growth = Math.round(stat.change! * multiple) / multiple;
|
||||
|
||||
this._chosenStat = stat;
|
||||
this._origAmount = growth;
|
||||
this._amount = growth;
|
||||
this._initDirtyTracking({ type: "deep" }, { amount: this._origAmount });
|
||||
this._initDirtyTracking({ type: "deep" }, { amount: growth });
|
||||
}
|
||||
|
||||
private _dateTimeSelectorChanged(ev) {
|
||||
@@ -393,7 +407,10 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends DirtyStateProvid
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.tools.tabs.statistics.fix_issue.adjust_sum.new_value"
|
||||
)}
|
||||
.selector=${this._amountSelector(unit || undefined, this._precision)}
|
||||
.selector=${this._amountSelector(
|
||||
unit || undefined,
|
||||
this._fractionDigits(this._chosenStat!.change)
|
||||
)}
|
||||
.value=${this._amount}
|
||||
.disabled=${this._busy}
|
||||
@value-changed=${this._amountChanged}
|
||||
@@ -572,7 +589,8 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends DirtyStateProvid
|
||||
this.hass,
|
||||
params.statistic.statistic_id,
|
||||
chosenStat.start,
|
||||
amount - this._origAmount!,
|
||||
// The shown value is rounded, so compare with the stored value.
|
||||
amount - chosenStat.change!,
|
||||
unit || null
|
||||
);
|
||||
} catch (err: any) {
|
||||
|
||||
@@ -1,257 +0,0 @@
|
||||
import type { ContextType } from "@lit/context";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { consume } from "../../../../common/decorators/consume";
|
||||
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/chart/statistics-chart";
|
||||
import "../../../../components/date-picker/ha-date-range-picker";
|
||||
import "../../../../components/ha-alert";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-dialog";
|
||||
import "../../../../components/ha-dialog-footer";
|
||||
import "../../../../components/ha-spinner";
|
||||
import {
|
||||
apiContext,
|
||||
internationalizationContext,
|
||||
} from "../../../../data/context";
|
||||
import type { Statistics, StatisticType } from "../../../../data/recorder";
|
||||
import {
|
||||
clearStatistics,
|
||||
fetchStatistics,
|
||||
StatisticMeanType,
|
||||
} from "../../../../data/recorder";
|
||||
import { DialogMixin } from "../../../../dialogs/dialog-mixin";
|
||||
import { haStyleDialog } from "../../../../resources/styles";
|
||||
import { showToast } from "../../../../util/toast";
|
||||
import type { DialogStatisticsClearRangeParams } from "./show-dialog-statistics-clear-range";
|
||||
|
||||
// Longer ranges use hourly statistics to keep the preview fast.
|
||||
const MAX_5MINUTE_PREVIEW_MS = 3 * 24 * 60 * 60 * 1000;
|
||||
|
||||
@customElement("dialog-statistics-clear-range")
|
||||
class DialogStatisticsClearRange extends DialogMixin<DialogStatisticsClearRangeParams>(
|
||||
LitElement
|
||||
) {
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: ContextType<typeof apiContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@state() private _start = new Date();
|
||||
|
||||
@state() private _end = new Date();
|
||||
|
||||
@state() private _statistics?: Statistics;
|
||||
|
||||
@state() private _busy = false;
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
// Incremented for every fetch, so results of outdated fetches are ignored
|
||||
private _fetchId = 0;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._end.setMinutes(
|
||||
this._end.getMinutes() - (this._end.getMinutes() % 5),
|
||||
0,
|
||||
0
|
||||
);
|
||||
this._start = new Date(this._end.getTime() - 24 * 60 * 60 * 1000);
|
||||
this._fetchStatistics();
|
||||
}
|
||||
|
||||
private get _validRange() {
|
||||
return this._start.getTime() < this._end.getTime();
|
||||
}
|
||||
|
||||
private get _statTypes(): StatisticType[] {
|
||||
return this.params!.statistic.mean_type !== StatisticMeanType.NONE
|
||||
? ["mean", "min", "max"]
|
||||
: ["change"];
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.params) {
|
||||
return nothing;
|
||||
}
|
||||
const localize = this._i18n.localize;
|
||||
const statisticId = this.params.statistic.statistic_id;
|
||||
const hasData = Boolean(this._statistics?.[statisticId]?.length);
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
width="large"
|
||||
.headerTitle=${localize(
|
||||
"ui.panel.config.tools.tabs.statistics.clear_range.title"
|
||||
)}
|
||||
.preventScrimClose=${this._busy}
|
||||
>
|
||||
<p>
|
||||
${localize(
|
||||
"ui.panel.config.tools.tabs.statistics.clear_range.info_text",
|
||||
{ name: html`<b>${this.params.name}</b>` }
|
||||
)}
|
||||
</p>
|
||||
<ha-date-range-picker
|
||||
time-picker
|
||||
.startDate=${this._start}
|
||||
.endDate=${this._end}
|
||||
.disabled=${this._busy}
|
||||
@value-changed=${this._rangeChanged}
|
||||
></ha-date-range-picker>
|
||||
${
|
||||
this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: nothing
|
||||
}
|
||||
${
|
||||
!this._validRange
|
||||
? html`<ha-alert alert-type="error">
|
||||
${localize(
|
||||
"ui.panel.config.tools.tabs.statistics.clear_range.invalid_range"
|
||||
)}
|
||||
</ha-alert>`
|
||||
: !this._statistics
|
||||
? html`<div class="preview center">
|
||||
<ha-spinner></ha-spinner>
|
||||
</div>`
|
||||
: !hasData
|
||||
? html`<div class="preview center">
|
||||
${localize(
|
||||
"ui.panel.config.tools.tabs.statistics.clear_range.no_statistics_found"
|
||||
)}
|
||||
</div>`
|
||||
: html`<div class="preview">
|
||||
<statistics-chart
|
||||
.statisticsData=${this._statistics}
|
||||
.metadata=${{ [statisticId]: this.params.statistic }}
|
||||
.names=${{ [statisticId]: this.params.name }}
|
||||
.statTypes=${this._statTypes}
|
||||
.chartType=${
|
||||
this._statTypes[0] === "change" ? "bar" : "line"
|
||||
}
|
||||
.startTime=${this._start}
|
||||
.endTime=${this._end}
|
||||
hide-legend
|
||||
.clickForMoreInfo=${false}
|
||||
height="240px"
|
||||
></statistics-chart>
|
||||
</div>`
|
||||
}
|
||||
<ha-dialog-footer slot="footer">
|
||||
<ha-button
|
||||
slot="secondaryAction"
|
||||
appearance="plain"
|
||||
.disabled=${this._busy}
|
||||
@click=${this.closeDialog}
|
||||
>
|
||||
${localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
slot="primaryAction"
|
||||
variant="danger"
|
||||
.loading=${this._busy}
|
||||
.disabled=${this._busy || !this._validRange || !hasData}
|
||||
@click=${this._delete}
|
||||
>
|
||||
${localize("ui.common.delete")}
|
||||
</ha-button>
|
||||
</ha-dialog-footer>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _rangeChanged(
|
||||
ev: HASSDomEvent<{ value: { startDate: Date; endDate: Date } }>
|
||||
) {
|
||||
this._start = ev.detail.value.startDate;
|
||||
this._end = ev.detail.value.endDate;
|
||||
this._fetchStatistics();
|
||||
}
|
||||
|
||||
private async _fetchStatistics() {
|
||||
const fetchId = ++this._fetchId;
|
||||
this._statistics = undefined;
|
||||
this._error = undefined;
|
||||
if (!this._validRange) {
|
||||
return;
|
||||
}
|
||||
const start = this._start;
|
||||
const end = this._end;
|
||||
try {
|
||||
const statistics = await fetchStatistics(
|
||||
this._api.callWS,
|
||||
start,
|
||||
end,
|
||||
[this.params!.statistic.statistic_id],
|
||||
end.getTime() - start.getTime() > MAX_5MINUTE_PREVIEW_MS
|
||||
? "hour"
|
||||
: "5minute",
|
||||
undefined,
|
||||
this._statTypes.includes("change") ? ["change"] : ["mean", "min", "max"]
|
||||
);
|
||||
if (fetchId === this._fetchId) {
|
||||
this._statistics = statistics;
|
||||
}
|
||||
} catch (err: any) {
|
||||
if (fetchId === this._fetchId) {
|
||||
this._statistics = {};
|
||||
this._error = err.message || err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _delete() {
|
||||
this._busy = true;
|
||||
this._error = undefined;
|
||||
try {
|
||||
await clearStatistics(this._api, [this.params!.statistic.statistic_id], {
|
||||
start: this._start,
|
||||
end: this._end,
|
||||
});
|
||||
} catch (err: any) {
|
||||
this._busy = false;
|
||||
this._error = err.message || err;
|
||||
return;
|
||||
}
|
||||
showToast(this, {
|
||||
message: this._i18n.localize(
|
||||
"ui.panel.config.tools.tabs.statistics.clear_range.deleted"
|
||||
),
|
||||
});
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
static styles = [
|
||||
haStyleDialog,
|
||||
css`
|
||||
p {
|
||||
margin-top: 0;
|
||||
}
|
||||
ha-date-range-picker {
|
||||
display: block;
|
||||
margin-bottom: var(--ha-space-4);
|
||||
}
|
||||
.preview {
|
||||
min-height: 240px;
|
||||
}
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-statistics-clear-range": DialogStatisticsClearRange;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { StatisticsMetaData } from "../../../../data/recorder";
|
||||
|
||||
export const loadClearRangeDialog = () =>
|
||||
import("./dialog-statistics-clear-range");
|
||||
|
||||
export interface DialogStatisticsClearRangeParams {
|
||||
statistic: StatisticsMetaData;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const showStatisticsClearRangeDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: DialogStatisticsClearRangeParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-statistics-clear-range",
|
||||
dialogImport: loadClearRangeDialog,
|
||||
dialogParams,
|
||||
});
|
||||
};
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
mdiArrowDown,
|
||||
mdiArrowUp,
|
||||
mdiClose,
|
||||
mdiDeleteClock,
|
||||
mdiFormatListChecks,
|
||||
mdiMenuDown,
|
||||
mdiSlopeUphill,
|
||||
@@ -79,7 +78,6 @@ import type { HomeAssistantRegistries } from "../../../../types";
|
||||
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||
import { fixStatisticsIssue } from "./fix-statistics";
|
||||
import { showStatisticsAdjustSumDialog } from "./show-dialog-statistics-adjust-sum";
|
||||
import { showStatisticsClearRangeDialog } from "./show-dialog-statistics-clear-range";
|
||||
|
||||
const FIX_ISSUES_ORDER: Record<StatisticsValidationResult["type"], number> = {
|
||||
no_state: 0,
|
||||
@@ -307,24 +305,6 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
|
||||
maxWidth: "113px",
|
||||
showNarrow: true,
|
||||
},
|
||||
clear_range: {
|
||||
title: "",
|
||||
label: localize(
|
||||
"ui.panel.config.tools.tabs.statistics.clear_range.title"
|
||||
),
|
||||
type: "icon-button",
|
||||
showNarrow: true,
|
||||
template: (statistic) => html`
|
||||
<ha-icon-button
|
||||
.label=${localize(
|
||||
"ui.panel.config.tools.tabs.statistics.clear_range.title"
|
||||
)}
|
||||
.path=${mdiDeleteClock}
|
||||
.statistic=${statistic}
|
||||
@click=${this._showStatisticsClearRangeDialog}
|
||||
></ha-icon-button>
|
||||
`,
|
||||
},
|
||||
actions: {
|
||||
title: "",
|
||||
label: localize("ui.panel.config.tools.tabs.statistics.adjust_sum"),
|
||||
@@ -747,19 +727,6 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
|
||||
});
|
||||
}
|
||||
|
||||
private _showStatisticsClearRangeDialog(
|
||||
ev: HASSDomCurrentTargetEvent<
|
||||
HTMLElement & { statistic: DisplayedStatisticData }
|
||||
>
|
||||
) {
|
||||
ev.stopPropagation();
|
||||
const statistic = ev.currentTarget.statistic;
|
||||
showStatisticsClearRangeDialog(this, {
|
||||
statistic,
|
||||
name: statistic.displayName,
|
||||
});
|
||||
}
|
||||
|
||||
private _rowClicked(ev: HASSDomEvent<RowClickedEvent>) {
|
||||
const id = ev.detail.id;
|
||||
if (id in this._states) {
|
||||
|
||||
@@ -4312,13 +4312,6 @@
|
||||
}
|
||||
},
|
||||
"adjust_sum": "Adjust sum",
|
||||
"clear_range": {
|
||||
"title": "Delete statistics in a time range",
|
||||
"info_text": "Delete the statistics of {name} between the start and end time. Use this to remove bad measurements from your graphs. Deleted statistics cannot be restored.",
|
||||
"invalid_range": "The end time must be after the start time.",
|
||||
"no_statistics_found": "No statistics found for this period.",
|
||||
"deleted": "Statistics deleted"
|
||||
},
|
||||
"data_table": {
|
||||
"select_all_issues": "Select all with issues",
|
||||
"name": "Name",
|
||||
@@ -4833,6 +4826,7 @@
|
||||
"environment": "Debug logging has been disabled, but no log file was downloaded because HA_DISABLE_LOG_FILE is set and Home Assistant is not writing a log file."
|
||||
},
|
||||
"scroll_down_button": "New logs - Click to scroll",
|
||||
"live": "Live",
|
||||
"provider_not_found": "Log provider not found",
|
||||
"provider_not_available": "Logs for ''{provider}'' are not available on your system.",
|
||||
"haos_boots_title": "Logs of HAOS startup",
|
||||
|
||||
Reference in new issue
Block a user