Compare commits

..

43 Commits

Author SHA1 Message Date
Aidan Timson
a812cf798d Remove header 2025-12-08 15:33:52 +00:00
Aidan Timson
23ecb4f412 Fix 2025-12-08 15:33:52 +00:00
Aidan Timson
5594401072 Remove use of slots 2025-12-08 15:33:52 +00:00
Aidan Timson
12bca642b2 Restore 2025-12-08 15:33:52 +00:00
Aidan Timson
9ec3f9251f Migrate dialog-labs-progress.ts from ha-md-dialog to ha-wa-dialog 2025-12-08 15:33:52 +00:00
Aidan Timson
3f0d424dbb Migrate dialog-labs-preview-feature-enable.ts from ha-md-dialog to ha-wa-dialog 2025-12-08 15:33:52 +00:00
Aidan Timson
b50daecdec Create an adaptive dialog and bottom sheet component, migrate restart (#28344)
* Create a switchable dialog and bottom sheet component

* Migrate dialog restart

* Fix close action

* Docstring

* Rename type

* Rename func

* Document

* Remove flex, use flex

* Cleanup docstring

* Update bodyScrolled

* Cleanup

* Remove

* Rename to ha-adaptive-dialog

* Cleanup

* Format

* Fix forwarding of slots

* Fix style

* listenMediaQuery, simplify and remove double scroll handling

* Add slotted footer to bottom sheet for compat

* Add block-mode-change to stop mode switches for forms etc.

* Document block-mode-change

* Update gallery

* Fix gallery doc for wa-dialog (caught by agent review)

* Update src/components/ha-adaptive-dialog.ts

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

* Cleanup

---------

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2025-12-08 17:32:31 +02:00
Aidan Timson
f5bd0816a8 Fix more info entity settings bottom fade (#28437)
* Use space tokens

* Fix settings bottom fade issue

* Space token

* Restore
2025-12-08 16:05:49 +02:00
Aidan Timson
b6d1e65044 Migrate dialog-edit-sidebar to ha-wa-dialog, fix dialog padding (#28426)
* Migrate dialog-edit-sidebar.ts from ha-md-dialog to ha-wa-dialog

* Format

* Fix wa dialog padding impl

* Remove custom size

* Use space tokens

* Remove render nothing

* Fix default top padding
2025-12-08 14:47:10 +02:00
Aidan Timson
e2312e52e3 Remove z-index from scrollable fades (#28434) 2025-12-08 14:33:30 +02:00
renovate[bot]
46a6da33d9 Update dependency terser-webpack-plugin to v5.3.15 (#28436)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-08 14:32:39 +02:00
Aidan Timson
e7b0b9a090 Add basic view transitions between tab UIs (#28374)
* Add basic view transitions between tab UIs

* Remove debug

* Add to debug navigation action
2025-12-08 14:22:05 +02:00
Petar Petrov
63ffc3b7f9 Restrict row resizing for stack and entities cards (#28422)
* Restrict row resizing for stack and entities cards

* Fix disabled state for grid layout slider
2025-12-08 12:53:37 +02:00
Nils Schönwald
ebfceae38c Update snowflake to 6 sides (#28406) 2025-12-08 09:16:55 +00:00
Aidan Timson
cccfc1716b Add scrollable fade mixin to bottom sheet, picker combo box (#28347)
* Add scrollable fade mixin to ha-bottom-sheet

* Wrap picker combobox (used inside of sheet with virtualizer)
2025-12-08 08:58:19 +02:00
dcapslock
6abf0dfcb9 Energy strategies to refresh energy collection which allows to be used in custom dashboards (#28400)
* Energy strategies to refresh energy collection which allows to be used in custom dashboards

* Update src/panels/energy/strategies/energy-overview-view-strategy.ts

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

* Only refresh if no prefs

---------

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2025-12-08 08:17:19 +02:00
dependabot[bot]
9d2404763f Bump home-assistant/wheels from 2025.11.0 to 2025.12.0 (#28421)
Bumps [home-assistant/wheels](https://github.com/home-assistant/wheels) from 2025.11.0 to 2025.12.0.
- [Release notes](https://github.com/home-assistant/wheels/releases)
- [Commits](https://github.com/home-assistant/wheels/compare/2025.11.0...2025.12.0)

---
updated-dependencies:
- dependency-name: home-assistant/wheels
  dependency-version: 2025.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-08 06:11:02 +00:00
dependabot[bot]
6c84c9b44e Bump github/codeql-action from 4.31.5 to 4.31.7 (#28417)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.5 to 4.31.7.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](fdbfb4d275...cf1bb45a27)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.31.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-08 06:10:48 +00:00
dependabot[bot]
edb2172bdd Bump actions/checkout from 6.0.0 to 6.0.1 (#28419)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.0 to 6.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](1af3b93b68...8e8c483db8)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-08 06:10:44 +00:00
dependabot[bot]
067e7645ac Bump actions/stale from 10.1.0 to 10.1.1 (#28420)
Bumps [actions/stale](https://github.com/actions/stale) from 10.1.0 to 10.1.1.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](5f858e3efb...997185467f)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-version: 10.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-08 06:10:35 +00:00
dependabot[bot]
c3e4af3db8 Bump actions/setup-node from 6.0.0 to 6.1.0 (#28418)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.0.0 to 6.1.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](2028fbc5c2...395ad32622)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-08 06:10:25 +00:00
karwosts
f44be2d3b9 More unsafe description_placeholders fixes (#28416) 2025-12-08 08:05:35 +02:00
karwosts
cff7ed9b05 Fix for undefined description_placeholders (#28395)
Another fix for undefined description_placeholders
2025-12-07 09:21:08 +02:00
Norbert Rittel
c4e5f1dba6 Fix wording for use with both energy and power sensors (#28392) 2025-12-06 13:41:56 +00:00
Bram Kragten
d1011d691f Handle search params changed after first updated for dashboards and … (#28375)
handle search params changed after first updated for dashboards and integrations
2025-12-06 13:45:44 +02:00
renovate[bot]
7dda8c36bc Update dependency prettier to v3.7.4 (#28388)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-06 09:32:57 +01:00
renovate[bot]
45b2376616 Update vitest monorepo to v4.0.15 (#28379)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 17:51:51 +01:00
Paul Bottein
2f70a82d02 Use non-admin endpoint to subscribe to one lab feature (#28352) 2025-12-05 17:34:22 +01:00
Aidan Timson
00868b2450 Add scrollable fade to more info dialog (#28314)
* Add scrollable fade to more info dialog

* Introduce scroll threshold (default 4px, more info 24px)

* Docstrings

* Remove getter for numeric values in mixin

* Update src/dialogs/more-info/ha-more-info-dialog.ts

Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com>

* Fix lint

---------

Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com>
2025-12-05 15:43:47 +01:00
Timothy
e573a726aa Add add to button in more info topbar for non admin users (#28365) 2025-12-05 15:05:50 +01:00
Petar Petrov
ef82bc2abb Fix calendar card not showing different colors for multiple calendars (#28338) 2025-12-05 13:39:51 +01:00
renovate[bot]
92e3864f63 Update dependency typescript-eslint to v8.48.1 (#28371)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 13:17:53 +01:00
renovate[bot]
a4af975bb3 Update dependency @rspack/core to v1.6.6 (#28370)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 13:17:47 +01:00
Aidan Timson
025ffd7b56 Revert "Fix underflowing text in ha-settings-row (#28339)" (#28369) 2025-12-05 11:50:37 +00:00
Jan-Philipp Benecke
3ea4a28931 Fix underflowing text in ha-settings-row (#28339) 2025-12-05 09:08:42 +00:00
Benjamin
3b092b834e Filter out hidden entities in map configuration (#28320) 2025-12-05 08:44:07 +01:00
Aidan Timson
ed8ccbe12c Add scrollable fade mixin to ha-wa-dialog (#28346) 2025-12-05 08:43:43 +01:00
karwosts
420f88f73a Fix incorrect water & gas price hints (#28357)
* Fix incorrect water price hint

* Gas
2025-12-05 08:44:23 +02:00
karwosts
086aa5fa28 Delete stop response variable on empty (#28362) 2025-12-05 08:38:39 +02:00
renovate[bot]
cca4cc512b Update dependency @rsdoctor/rspack-plugin to v1.3.12 (#28350)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-04 14:54:27 +00:00
Petar Petrov
8eb65f327a Append current state to power-sources-graph (#28330) 2025-12-04 10:18:48 +01:00
Petar Petrov
f3495feacb Fix markdown sections and styling (#28333) 2025-12-04 10:15:11 +01:00
Petar Petrov
2161bcfa3f Fix gauge severity using entity state instead of attribute value (#28331) 2025-12-04 10:13:41 +01:00
62 changed files with 2267 additions and 1100 deletions

View File

@@ -21,12 +21,12 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
ref: dev
- name: Setup Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -56,12 +56,12 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
ref: master
- name: Setup Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -24,9 +24,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -58,9 +58,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -76,9 +76,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -100,9 +100,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -23,7 +23,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
@@ -36,14 +36,14 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
uses: github/codeql-action/init@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
with:
languages: ${{ matrix.language }}
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
uses: github/codeql-action/autobuild@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -57,4 +57,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
uses: github/codeql-action/analyze@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7

View File

@@ -22,12 +22,12 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
ref: dev
- name: Setup Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -57,12 +57,12 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
ref: master
- name: Setup Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -16,10 +16,10 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -21,10 +21,10 @@ jobs:
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
steps:
- name: Check out files from GitHub
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -20,7 +20,7 @@ jobs:
contents: write
steps:
- name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6
@@ -28,7 +28,7 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -23,7 +23,7 @@ jobs:
contents: write # Required to upload release assets
steps:
- name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
@@ -34,7 +34,7 @@ jobs:
uses: home-assistant/actions/helpers/verify-version@master
- name: Setup Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -75,7 +75,7 @@ jobs:
# home-assistant/wheels doesn't support SHA pinning
- name: Build wheels
uses: home-assistant/wheels@2025.11.0
uses: home-assistant/wheels@2025.12.0
with:
abi: cp313
tag: musllinux_1_2
@@ -91,9 +91,9 @@ jobs:
contents: write # Required to upload release assets
steps:
- name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -120,9 +120,9 @@ jobs:
contents: write # Required to upload release assets
steps:
- name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 90 days stale policy
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 90

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Upload Translations
run: |

View File

@@ -0,0 +1,3 @@
---
title: Adaptive dialog (ha-adaptive-dialog)
---

View File

@@ -0,0 +1,732 @@
import { css, html, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
import { mdiCog, mdiHelp } from "@mdi/js";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-dialog-footer";
import "../../../../src/components/ha-adaptive-dialog";
import "../../../../src/components/ha-form/ha-form";
import "../../../../src/components/ha-icon-button";
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import type { HomeAssistant } from "../../../../src/types";
const SCHEMA: HaFormSchema[] = [
{ type: "string", name: "Name", default: "", autofocus: true },
{ type: "string", name: "Email", default: "" },
];
type DialogType =
| false
| "basic"
| "basic-subtitle-below"
| "basic-subtitle-above"
| "form"
| "form-block-mode"
| "actions"
| "large"
| "small";
@customElement("demo-components-ha-adaptive-dialog")
export class DemoHaAdaptiveDialog extends LitElement {
@state() private _openDialog: DialogType = false;
@state() private _hass?: HomeAssistant;
protected firstUpdated() {
const hass = provideHass(this);
this._hass = hass;
}
protected render() {
return html`
<div class="content">
<h1>Adaptive dialog <code>&lt;ha-adaptive-dialog&gt;</code></h1>
<p class="subtitle">
Responsive dialog component that automatically switches between a full
dialog and bottom sheet based on screen size.
</p>
<h2>Demos</h2>
<div class="buttons">
<ha-button @click=${this._handleOpenDialog("basic")}
>Basic adaptive dialog</ha-button
>
<ha-button @click=${this._handleOpenDialog("basic-subtitle-below")}
>Adaptive dialog with subtitle below</ha-button
>
<ha-button @click=${this._handleOpenDialog("basic-subtitle-above")}
>Adaptive dialog with subtitle above</ha-button
>
<ha-button @click=${this._handleOpenDialog("small")}
>Small width adaptive dialog</ha-button
>
<ha-button @click=${this._handleOpenDialog("large")}
>Large width adaptive dialog</ha-button
>
<ha-button @click=${this._handleOpenDialog("form")}
>Adaptive dialog with form</ha-button
>
<ha-button @click=${this._handleOpenDialog("form-block-mode")}
>Adaptive dialog with form (block mode change)</ha-button
>
<ha-button @click=${this._handleOpenDialog("actions")}
>Adaptive dialog with actions</ha-button
>
</div>
<ha-card>
<div class="card-content">
<p>
<strong>Tip:</strong> Resize your browser window to see the
responsive behavior. The dialog automatically switches to a bottom
sheet on narrow screens (&lt;870px width) or short screens
(&lt;500px height).
</p>
</div>
</ha-card>
<ha-adaptive-dialog
.hass=${this._hass}
.open=${this._openDialog === "basic"}
header-title="Basic adaptive dialog"
@closed=${this._handleClosed}
>
<div>Adaptive dialog content</div>
</ha-adaptive-dialog>
<ha-adaptive-dialog
.hass=${this._hass}
.open=${this._openDialog === "basic-subtitle-below"}
header-title="Adaptive dialog with subtitle"
header-subtitle="This is an adaptive dialog with a subtitle below"
@closed=${this._handleClosed}
>
<div>Adaptive dialog content</div>
</ha-adaptive-dialog>
<ha-adaptive-dialog
.hass=${this._hass}
.open=${this._openDialog === "basic-subtitle-above"}
header-title="Adaptive dialog with subtitle above"
header-subtitle="This is an adaptive dialog with a subtitle above"
header-subtitle-position="above"
@closed=${this._handleClosed}
>
<div>Adaptive dialog content</div>
</ha-adaptive-dialog>
<ha-adaptive-dialog
.hass=${this._hass}
.open=${this._openDialog === "small"}
width="small"
header-title="Small adaptive dialog"
@closed=${this._handleClosed}
>
<div>This dialog uses the small width preset (320px).</div>
</ha-adaptive-dialog>
<ha-adaptive-dialog
.hass=${this._hass}
.open=${this._openDialog === "large"}
width="large"
header-title="Large adaptive dialog"
@closed=${this._handleClosed}
>
<div>This dialog uses the large width preset (1024px).</div>
</ha-adaptive-dialog>
<ha-adaptive-dialog
.hass=${this._hass}
.open=${this._openDialog === "form"}
header-title="Adaptive dialog with form"
header-subtitle="This is an adaptive dialog with a form"
@closed=${this._handleClosed}
>
<ha-form autofocus .schema=${SCHEMA}></ha-form>
<ha-dialog-footer slot="footer">
<ha-button
@click=${this._handleClosed}
slot="secondaryAction"
variant="plain"
>Cancel</ha-button
>
<ha-button
@click=${this._handleClosed}
slot="primaryAction"
variant="accent"
>Submit</ha-button
>
</ha-dialog-footer>
</ha-adaptive-dialog>
<ha-adaptive-dialog
.hass=${this._hass}
.open=${this._openDialog === "form-block-mode"}
header-title="Adaptive dialog with form (block mode change)"
header-subtitle="This form will not reset when the viewport size changes"
block-mode-change
@closed=${this._handleClosed}
>
<ha-form autofocus .schema=${SCHEMA}></ha-form>
<ha-dialog-footer slot="footer">
<ha-button
@click=${this._handleClosed}
slot="secondaryAction"
variant="plain"
>Cancel</ha-button
>
<ha-button
@click=${this._handleClosed}
slot="primaryAction"
variant="accent"
>Submit</ha-button
>
</ha-dialog-footer>
</ha-adaptive-dialog>
<ha-adaptive-dialog
.hass=${this._hass}
.open=${this._openDialog === "actions"}
header-title="Adaptive dialog with actions"
header-subtitle="This is an adaptive dialog with header actions"
@closed=${this._handleClosed}
>
<div slot="headerActionItems">
<ha-icon-button label="Settings" path=${mdiCog}></ha-icon-button>
<ha-icon-button label="Help" path=${mdiHelp}></ha-icon-button>
</div>
<div>Adaptive dialog content</div>
</ha-adaptive-dialog>
<h2>Design</h2>
<h3>Responsive behavior</h3>
<p>
The <code>ha-adaptive-dialog</code> component automatically switches
between two modes based on screen size:
</p>
<ul>
<li>
<strong>Dialog mode:</strong> Used on larger screens (width &gt;
870px and height &gt; 500px). Renders as a centered dialog using
<code>ha-wa-dialog</code>.
</li>
<li>
<strong>Bottom sheet mode:</strong> Used on mobile devices and
smaller screens (width ≤ 870px or height ≤ 500px). Renders as a
drawer from the bottom using <code>ha-bottom-sheet</code>.
</li>
</ul>
<p>
The mode is determined automatically and updates when the window is
resized. To prevent mode changes after the initial mount (useful for
preventing form resets), use the <code>block-mode-change</code>
attribute.
</p>
<h3>Width</h3>
<p>
In dialog mode, there are multiple width presets available. These are
ignored in bottom sheet mode.
</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>small</code></td>
<td><code>min(320px, var(--full-width))</code></td>
</tr>
<tr>
<td><code>medium</code></td>
<td><code>min(580px, var(--full-width))</code></td>
</tr>
<tr>
<td><code>large</code></td>
<td><code>min(1024px, var(--full-width))</code></td>
</tr>
<tr>
<td><code>full</code></td>
<td><code>var(--full-width)</code></td>
</tr>
</tbody>
</table>
<p>Adaptive dialogs have a default width of <code>medium</code>.</p>
<h3>Header</h3>
<p>
The header contains a navigation icon, title, subtitle, and action
items.
</p>
<table>
<thead>
<tr>
<th>Slot</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>headerNavigationIcon</code></td>
<td>
Leading header action (e.g., close/back button). In bottom sheet
mode, defaults to a close button if not provided.
</td>
</tr>
<tr>
<td><code>headerTitle</code></td>
<td>The header title content.</td>
</tr>
<tr>
<td><code>headerSubtitle</code></td>
<td>The header subtitle content.</td>
</tr>
<tr>
<td><code>headerActionItems</code></td>
<td>Trailing header actions (e.g., icon buttons, menus).</td>
</tr>
</tbody>
</table>
<h4>Header title</h4>
<p>
The header title can be set using the <code>header-title</code>
attribute or by providing custom content in the
<code>headerTitle</code> slot.
</p>
<h4>Header subtitle</h4>
<p>
The header subtitle can be set using the
<code>header-subtitle</code> attribute or by providing custom content
in the <code>headerSubtitle</code> slot. The subtitle position
relative to the title can be controlled with the
<code>header-subtitle-position</code> attribute.
</p>
<h4>Header navigation icon</h4>
<p>
In bottom sheet mode, a close button is automatically provided if no
custom navigation icon is specified. In dialog mode, the dialog can be
closed via the standard dialog close button.
</p>
<h4>Header action items</h4>
<p>
The header action items usually contain icon buttons and/or menu
buttons.
</p>
<h3>Body</h3>
<p>The body is the content of the adaptive dialog.</p>
<h3>Footer</h3>
<p>The footer is the footer of the adaptive dialog.</p>
<p>
It is recommended to use the <code>ha-dialog-footer</code> component
for the footer and to style the buttons inside the footer as follows:
</p>
<table>
<thead>
<tr>
<th>Slot</th>
<th>Description</th>
<th>Variant to use</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>secondaryAction</code></td>
<td>The secondary action button(s).</td>
<td><code>plain</code></td>
</tr>
<tr>
<td><code>primaryAction</code></td>
<td>The primary action button(s).</td>
<td><code>accent</code></td>
</tr>
</tbody>
</table>
<h2>Implementation</h2>
<h3>When to use</h3>
<p>
Use <code>ha-adaptive-dialog</code> when you need a dialog that should
adapt to different screen sizes automatically. This is particularly
useful for:
</p>
<ul>
<li>Forms and data entry that need to work well on mobile devices</li>
<li>
Content that benefits from full-screen presentation on small devices
</li>
<li>
Interfaces that need consistent behavior across desktop and mobile
</li>
</ul>
<p>
If you don't need responsive behavior, use
<code>ha-wa-dialog</code> directly for desktop-only dialogs or
<code>ha-bottom-sheet</code> for mobile-only sheets.
</p>
<p>
Use the <code>block-mode-change</code> attribute when you want to
prevent the dialog from switching modes after it's opened. This is
especially useful for forms, as it prevents form data from being lost
when users resize their browser window.
</p>
<h3>Example usage</h3>
<pre><code>&lt;ha-adaptive-dialog
.hass=\${this.hass}
open
width="medium"
header-title="Dialog title"
header-subtitle="Dialog subtitle"
&gt;
&lt;div slot="headerActionItems"&gt;
&lt;ha-icon-button label="Settings" path="mdiCog"&gt;&lt;/ha-icon-button&gt;
&lt;ha-icon-button label="Help" path="mdiHelp"&gt;&lt;/ha-icon-button&gt;
&lt;/div&gt;
&lt;div&gt;Dialog content&lt;/div&gt;
&lt;ha-dialog-footer slot="footer"&gt;
&lt;ha-button slot="secondaryAction" variant="plain"
&gt;Cancel&lt;/ha-button
&gt;
&lt;ha-button slot="primaryAction" variant="accent"&gt;Submit&lt;/ha-button&gt;
&lt;/ha-dialog-footer&gt;
&lt;/ha-adaptive-dialog&gt;</code></pre>
<p>Example with <code>block-mode-change</code> for forms:</p>
<pre><code>&lt;ha-adaptive-dialog
.hass=\${this.hass}
open
header-title="Edit configuration"
block-mode-change
&gt;
&lt;ha-form .schema=\${schema} .data=\${data}&gt;&lt;/ha-form&gt;
&lt;ha-dialog-footer slot="footer"&gt;
&lt;ha-button slot="secondaryAction" variant="plain"
&gt;Cancel&lt;/ha-button
&gt;
&lt;ha-button slot="primaryAction" variant="accent"&gt;Save&lt;/ha-button&gt;
&lt;/ha-dialog-footer&gt;
&lt;/ha-adaptive-dialog&gt;</code></pre>
<h3>API</h3>
<p>
This component combines <code>ha-wa-dialog</code> and
<code>ha-bottom-sheet</code> with automatic mode switching based on
screen size.
</p>
<h4>Attributes</h4>
<table>
<thead>
<tr>
<th>Attribute</th>
<th>Description</th>
<th>Default</th>
<th>Options</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>open</code></td>
<td>Controls the adaptive dialog open state.</td>
<td><code>false</code></td>
<td><code>false</code>, <code>true</code></td>
</tr>
<tr>
<td><code>width</code></td>
<td>
Preferred dialog width preset (dialog mode only, ignored in
bottom sheet mode).
</td>
<td><code>medium</code></td>
<td>
<code>small</code>, <code>medium</code>, <code>large</code>,
<code>full</code>
</td>
</tr>
<tr>
<td><code>header-title</code></td>
<td>Header title text when no custom title slot is provided.</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>header-subtitle</code></td>
<td>
Header subtitle text when no custom subtitle slot is provided.
</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>header-subtitle-position</code></td>
<td>Position of the subtitle relative to the title.</td>
<td><code>below</code></td>
<td><code>above</code>, <code>below</code></td>
</tr>
<tr>
<td><code>aria-labelledby</code></td>
<td>
The ID of the element that labels the dialog (for
accessibility).
</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>aria-describedby</code></td>
<td>
The ID of the element that describes the dialog (for
accessibility).
</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>block-mode-change</code></td>
<td>
When set, the mode is determined at mount time based on the
current screen size, but subsequent mode changes are blocked.
Useful for preventing forms from resetting when the viewport
size changes.
</td>
<td><code>false</code></td>
<td><code>false</code>, <code>true</code></td>
</tr>
</tbody>
</table>
<h4>CSS custom properties</h4>
<table>
<thead>
<tr>
<th>CSS Property</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>--ha-dialog-surface-background</code></td>
<td>Dialog/sheet background color.</td>
</tr>
<tr>
<td><code>--ha-dialog-border-radius</code></td>
<td>Border radius of the dialog surface (dialog mode only).</td>
</tr>
<tr>
<td><code>--ha-dialog-show-duration</code></td>
<td>Show animation duration (dialog mode only).</td>
</tr>
<tr>
<td><code>--ha-dialog-hide-duration</code></td>
<td>Hide animation duration (dialog mode only).</td>
</tr>
</tbody>
</table>
<h4>Events</h4>
<table>
<thead>
<tr>
<th>Event</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>opened</code></td>
<td>
Fired when the adaptive dialog is shown (dialog mode only).
</td>
</tr>
<tr>
<td><code>closed</code></td>
<td>
Fired after the adaptive dialog is hidden (dialog mode only).
</td>
</tr>
<tr>
<td><code>after-show</code></td>
<td>Fired after show animation completes (dialog mode only).</td>
</tr>
</tbody>
</table>
<h3>Focus management</h3>
<p>
To automatically focus an element when the adaptive dialog opens, add
the
<code>autofocus</code> attribute to it. Components with
<code>delegatesFocus: true</code> (like <code>ha-form</code>) will
forward focus to their first focusable child.
</p>
<p>Example:</p>
<pre><code>&lt;ha-adaptive-dialog .hass=\${this.hass} open&gt;
&lt;ha-form autofocus .schema=\${schema}&gt;&lt;/ha-form&gt;
&lt;/ha-adaptive-dialog&gt;</code></pre>
</div>
`;
}
private _handleOpenDialog = (dialog: DialogType) => () => {
this._openDialog = dialog;
};
private _handleClosed = () => {
this._openDialog = false;
};
static styles = [
css`
:host {
display: block;
padding: var(--ha-space-4);
}
.content {
max-width: 1000px;
margin: 0 auto;
}
h1 {
margin-top: 0;
margin-bottom: var(--ha-space-2);
}
h2 {
margin-top: var(--ha-space-6);
margin-bottom: var(--ha-space-3);
}
h3,
h4 {
margin-top: var(--ha-space-4);
margin-bottom: var(--ha-space-2);
}
p {
margin: var(--ha-space-2) 0;
line-height: 1.6;
}
ul {
margin: var(--ha-space-2) 0;
padding-left: var(--ha-space-5);
}
li {
margin: var(--ha-space-1) 0;
line-height: 1.6;
}
.subtitle {
color: var(--secondary-text-color);
font-size: 1.1em;
margin-bottom: var(--ha-space-4);
}
table {
width: 100%;
border-collapse: collapse;
margin: var(--ha-space-3) 0;
}
th,
td {
text-align: left;
padding: var(--ha-space-2);
border-bottom: 1px solid var(--divider-color);
}
th {
font-weight: 500;
}
code {
background-color: var(--secondary-background-color);
padding: 2px 6px;
border-radius: 4px;
font-family: monospace;
font-size: 0.9em;
}
pre {
background-color: var(--secondary-background-color);
padding: var(--ha-space-3);
border-radius: 8px;
overflow-x: auto;
margin: var(--ha-space-3) 0;
}
pre code {
background-color: transparent;
padding: 0;
}
.buttons {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: var(--ha-space-2);
margin: var(--ha-space-4) 0;
}
.card-content {
padding: var(--ha-space-3);
}
a {
color: var(--primary-color);
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"demo-components-ha-adaptive-dialog": DemoHaAdaptiveDialog;
}
}

View File

@@ -139,7 +139,7 @@ export class DemoHaWaDialog extends LitElement {
</tr>
<tr>
<td><code>large</code></td>
<td><code>min(720px, var(--full-width))</code></td>
<td><code>min(1024px, var(--full-width))</code></td>
</tr>
<tr>
<td><code>full</code></td>

View File

@@ -157,8 +157,8 @@
"@octokit/auth-oauth-device": "8.0.3",
"@octokit/plugin-retry": "8.0.3",
"@octokit/rest": "22.0.1",
"@rsdoctor/rspack-plugin": "1.3.11",
"@rspack/core": "1.6.5",
"@rsdoctor/rspack-plugin": "1.3.12",
"@rspack/core": "1.6.6",
"@rspack/dev-server": "1.1.4",
"@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.22",
@@ -178,7 +178,7 @@
"@types/tar": "6.1.13",
"@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29",
"@vitest/coverage-v8": "4.0.14",
"@vitest/coverage-v8": "4.0.15",
"babel-loader": "10.0.0",
"babel-plugin-template-html-minifier": "4.1.0",
"browserslist-useragent-regexp": "4.1.3",
@@ -209,17 +209,17 @@
"lodash.template": "4.5.0",
"map-stream": "0.0.7",
"pinst": "3.0.0",
"prettier": "3.7.3",
"prettier": "3.7.4",
"rspack-manifest-plugin": "5.2.0",
"serve": "14.2.5",
"sinon": "21.0.0",
"tar": "7.5.2",
"terser-webpack-plugin": "5.3.14",
"terser-webpack-plugin": "5.3.15",
"ts-lit-plugin": "2.0.2",
"typescript": "5.9.3",
"typescript-eslint": "8.48.0",
"typescript-eslint": "8.48.1",
"vite-tsconfig-paths": "5.1.4",
"vitest": "4.0.14",
"vitest": "4.0.15",
"webpack-stats-plugin": "1.1.3",
"webpackbar": "7.0.0",
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"

View File

@@ -2,17 +2,15 @@ import type { TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import type { LabelRegistryEntry } from "../../data/label_registry";
import { computeCssColor } from "../../common/color/compute-color";
import { fireEvent } from "../../common/dom/fire_event";
import { stopPropagation } from "../../common/dom/stop_propagation";
import { stringCompare } from "../../common/string/compare";
import type { LabelRegistryEntry } from "../../data/label_registry";
import "../chips/ha-chip-set";
import "../ha-dropdown";
import "../ha-dropdown-item";
import type { HaDropdownItem } from "../ha-dropdown-item";
import "../ha-icon";
import "../ha-label";
import { stringCompare } from "../../common/string/compare";
import "../chips/ha-chip-set";
import "../ha-button-menu";
import "../ha-icon";
import "../ha-list-item";
@customElement("ha-data-table-labels")
class HaDataTableLabels extends LitElement {
@@ -28,11 +26,12 @@ class HaDataTableLabels extends LitElement {
(label) => this._renderLabel(label, true)
)}
${labels.length > 2
? html`<ha-dropdown
? html`<ha-button-menu
absolute
role="button"
tabindex="0"
@click=${stopPropagation}
@wa-select=${this._handleDropdownSelect}
@click=${this._handleIconOverflowMenuOpened}
@closed=${this._handleIconOverflowMenuClosed}
>
<ha-label slot="trigger" class="plus" dense>
+${labels.length - 2}
@@ -41,12 +40,12 @@ class HaDataTableLabels extends LitElement {
labels.slice(2),
(label) => label.label_id,
(label) => html`
<ha-dropdown-item .value=${label.label_id} .item=${label}>
<ha-list-item @click=${this._labelClicked} .item=${label}>
${this._renderLabel(label, false)}
</ha-dropdown-item>
</ha-list-item>
`
)}
</ha-dropdown>`
</ha-button-menu>`
: nothing}
</ha-chip-set>
`;
@@ -82,12 +81,21 @@ class HaDataTableLabels extends LitElement {
fireEvent(this, "label-clicked", { label });
}
private _handleDropdownSelect(
ev: CustomEvent<{ item: HaDropdownItem & { item?: LabelRegistryEntry } }>
) {
const label = ev.detail?.item?.item;
if (label) {
fireEvent(this, "label-clicked", { label });
protected _handleIconOverflowMenuOpened(e) {
e.stopPropagation();
// If this component is used inside a data table, the z-index of the row
// needs to be increased. Otherwise the ha-button-menu would be displayed
// underneath the next row in the table.
const row = this.closest(".mdc-data-table__row") as HTMLDivElement | null;
if (row) {
row.style.zIndex = "1";
}
}
protected _handleIconOverflowMenuClosed() {
const row = this.closest(".mdc-data-table__row") as HTMLDivElement | null;
if (row) {
row.style.zIndex = "";
}
}
@@ -106,6 +114,9 @@ class HaDataTableLabels extends LitElement {
--ha-label-background-color: var(--color, var(--grey-color));
--ha-label-background-opacity: 0.5;
}
ha-button-menu {
border-radius: 10px;
}
.plus {
--ha-label-background-color: transparent;
border: 1px solid var(--divider-color);

View File

@@ -0,0 +1,188 @@
import { mdiClose } from "@mdi/js";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import type { HomeAssistant } from "../types";
import { listenMediaQuery } from "../common/dom/media_query";
import "./ha-bottom-sheet";
import "./ha-dialog-header";
import "./ha-icon-button";
import "./ha-wa-dialog";
import type { DialogWidth } from "./ha-wa-dialog";
type DialogSheetMode = "dialog" | "bottom-sheet";
/**
* Home Assistant adaptive dialog component
*
* @element ha-adaptive-dialog
* @extends {LitElement}
*
* @summary
* A responsive dialog component that automatically switches between a full dialog (ha-wa-dialog)
* and a bottom sheet (ha-bottom-sheet) based on screen size. Uses dialog mode on larger screens
* (>870px width and >500px height) and bottom sheet mode on smaller screens or mobile devices.
*
* @slot headerNavigationIcon - Leading header action (e.g. close/back button).
* @slot headerTitle - Custom title content (used when header-title is not set).
* @slot headerSubtitle - Custom subtitle content (used when header-subtitle is not set).
* @slot headerActionItems - Trailing header actions (e.g. buttons, menus).
* @slot - Dialog/sheet content body.
* @slot footer - Dialog/sheet footer content.
*
* @cssprop --ha-dialog-surface-background - Dialog/sheet background color.
* @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).
*
* @attr {boolean} open - Controls the dialog/sheet open state.
* @attr {("small"|"medium"|"large"|"full")} width - Preferred dialog width preset (dialog mode only). Defaults to "medium".
* @attr {string} header-title - Header title text. If not set, the headerTitle slot is used.
* @attr {string} header-subtitle - Header subtitle text. If not set, the headerSubtitle slot is used.
* @attr {("above"|"below")} header-subtitle-position - Position of the subtitle relative to the title. Defaults to "below".
* @attr {boolean} block-mode-change - When set, the mode is determined at mount time based on the current screen size, but subsequent mode changes are blocked. Useful for preventing forms from resetting when the viewport size changes.
*
* @event opened - Fired when the dialog/sheet is shown (dialog mode only).
* @event closed - Fired after the dialog/sheet is hidden.
* @event after-show - Fired after show animation completes (dialog mode only).
*
* @remarks
* **Responsive Behavior:**
* The component automatically switches between dialog and bottom sheet modes based on viewport size.
* Dialog mode is used for screens wider than 870px and taller than 500px.
* Bottom sheet mode is used for mobile devices and smaller screens.
*
* When `block-mode-change` is set, the mode is determined once at mount time based on the initial
* screen size. Subsequent viewport size changes will not trigger mode switches, which is useful
* for preventing form resets or other state loss when users resize their browser window.
*
* **Focus Management:**
* To automatically focus an element when opened, add the `autofocus` attribute to it.
* Components with `delegatesFocus: true` (like `ha-form`) will forward focus to their first focusable child.
* Example: `<ha-form autofocus .schema=${schema}></ha-form>`
*/
@customElement("ha-adaptive-dialog")
export class HaAdaptiveDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: "aria-labelledby" })
public ariaLabelledBy?: string;
@property({ attribute: "aria-describedby" })
public ariaDescribedBy?: string;
@property({ type: Boolean, reflect: true })
public open = false;
@property({ type: String, reflect: true, attribute: "width" })
public width: DialogWidth = "medium";
@property({ attribute: "header-title" })
public headerTitle?: string;
@property({ attribute: "header-subtitle" })
public headerSubtitle?: string;
@property({ type: String, attribute: "header-subtitle-position" })
public headerSubtitlePosition: "above" | "below" = "below";
@property({ type: Boolean, attribute: "block-mode-change" })
public blockModeChange = false;
@state() private _mode: DialogSheetMode = "dialog";
private _unsubMediaQuery?: () => void;
private _modeSet = false;
connectedCallback() {
super.connectedCallback();
this._unsubMediaQuery = listenMediaQuery(
"(max-width: 870px), (max-height: 500px)",
(matches) => {
if (!this._modeSet || !this.blockModeChange) {
this._mode = matches ? "bottom-sheet" : "dialog";
this._modeSet = true;
}
}
);
}
disconnectedCallback() {
super.disconnectedCallback();
this._unsubMediaQuery?.();
this._unsubMediaQuery = undefined;
this._modeSet = false;
}
render() {
if (this._mode === "bottom-sheet") {
return html`
<ha-bottom-sheet .open=${this.open} flexcontent>
<ha-dialog-header
slot="header"
.subtitlePosition=${this.headerSubtitlePosition}
>
<slot name="headerNavigationIcon" slot="navigationIcon">
<ha-icon-button
data-drawer="close"
.label=${this.hass?.localize("ui.common.close") ?? "Close"}
.path=${mdiClose}
></ha-icon-button>
</slot>
${this.headerTitle !== undefined
? html`<span slot="title" class="title" id="ha-wa-dialog-title">
${this.headerTitle}
</span>`
: html`<slot name="headerTitle" slot="title"></slot>`}
${this.headerSubtitle !== undefined
? html`<span slot="subtitle">${this.headerSubtitle}</span>`
: html`<slot name="headerSubtitle" slot="subtitle"></slot>`}
<slot name="headerActionItems" slot="actionItems"></slot>
</ha-dialog-header>
<slot></slot>
<slot name="footer" slot="footer"></slot>
</ha-bottom-sheet>
`;
}
return html`
<ha-wa-dialog
.hass=${this.hass}
.open=${this.open}
.width=${this.width}
.ariaLabelledBy=${this.ariaLabelledBy}
.ariaDescribedBy=${this.ariaDescribedBy}
.headerTitle=${this.headerTitle}
.headerSubtitle=${this.headerSubtitle}
.headerSubtitlePosition=${this.headerSubtitlePosition}
flexcontent
>
<slot name="headerNavigationIcon" slot="headerNavigationIcon"></slot>
<slot name="headerTitle" slot="headerTitle"></slot>
<slot name="headerSubtitle" slot="headerSubtitle"></slot>
<slot name="headerActionItems" slot="headerActionItems"></slot>
<slot></slot>
<slot name="footer" slot="footer"></slot>
</ha-wa-dialog>
`;
}
static get styles() {
return [
css`
ha-bottom-sheet {
--ha-bottom-sheet-surface-background: var(
--ha-dialog-surface-background,
var(--card-background-color, var(--ha-color-surface-default))
);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-adaptive-dialog": HaAdaptiveDialog;
}
}

View File

@@ -2,12 +2,13 @@ import "@home-assistant/webawesome/dist/components/drawer/drawer";
import { css, html, LitElement, type PropertyValues } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { SwipeGestureRecognizer } from "../common/util/swipe-gesture-recognizer";
import { ScrollableFadeMixin } from "../mixins/scrollable-fade-mixin";
import { haStyleScrollbar } from "../resources/styles";
export const BOTTOM_SHEET_ANIMATION_DURATION_MS = 300;
@customElement("ha-bottom-sheet")
export class HaBottomSheet extends LitElement {
export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
@property({ type: Boolean }) public open = false;
@property({ type: Boolean, reflect: true, attribute: "flexcontent" })
@@ -17,6 +18,12 @@ export class HaBottomSheet extends LitElement {
@query("#drawer") private _drawer!: HTMLElement;
@query("#body") private _bodyElement!: HTMLDivElement;
protected get scrollableElement(): HTMLElement | null {
return this._bodyElement;
}
private _gestureRecognizer = new SwipeGestureRecognizer();
private _isDragging = false;
@@ -49,9 +56,13 @@ export class HaBottomSheet extends LitElement {
@touchstart=${this._handleTouchStart}
>
<slot name="header"></slot>
<div id="body" class="body ha-scrollbar">
<slot></slot>
<div class="content-wrapper">
<div id="body" class="body ha-scrollbar">
<slot></slot>
</div>
${this.renderScrollableFades()}
</div>
<slot name="footer"></slot>
</wa-drawer>
`;
}
@@ -167,60 +178,87 @@ export class HaBottomSheet extends LitElement {
this._isDragging = false;
}
static styles = [
haStyleScrollbar,
css`
wa-drawer {
--wa-color-surface-raised: transparent;
--spacing: 0;
--size: var(--ha-bottom-sheet-height, auto);
--show-duration: ${BOTTOM_SHEET_ANIMATION_DURATION_MS}ms;
--hide-duration: ${BOTTOM_SHEET_ANIMATION_DURATION_MS}ms;
}
wa-drawer::part(dialog) {
max-height: var(--ha-bottom-sheet-max-height, 90vh);
align-items: center;
transform: var(--dialog-transform);
transition: var(--dialog-transition);
}
wa-drawer::part(body) {
max-width: var(--ha-bottom-sheet-max-width);
width: 100%;
border-top-left-radius: var(
--ha-bottom-sheet-border-radius,
var(--ha-dialog-border-radius, var(--ha-border-radius-2xl))
);
border-top-right-radius: var(
--ha-bottom-sheet-border-radius,
var(--ha-dialog-border-radius, var(--ha-border-radius-2xl))
);
background-color: var(
--ha-bottom-sheet-surface-background,
var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff)),
);
padding: var(
--ha-bottom-sheet-padding,
0 var(--safe-area-inset-right) var(--safe-area-inset-bottom)
var(--safe-area-inset-left)
);
}
:host([flexcontent]) wa-drawer::part(body) {
display: flex;
flex-direction: column;
}
:host([flexcontent]) .body {
flex: 1;
max-width: 100%;
display: flex;
flex-direction: column;
padding: var(
--ha-bottom-sheet-padding,
0 var(--safe-area-inset-right) var(--safe-area-inset-bottom)
var(--safe-area-inset-left)
);
}
`,
];
static get styles() {
return [
...super.styles,
haStyleScrollbar,
css`
wa-drawer {
--wa-color-surface-raised: transparent;
--spacing: 0;
--size: var(--ha-bottom-sheet-height, auto);
--show-duration: ${BOTTOM_SHEET_ANIMATION_DURATION_MS}ms;
--hide-duration: ${BOTTOM_SHEET_ANIMATION_DURATION_MS}ms;
}
wa-drawer::part(dialog) {
max-height: var(--ha-bottom-sheet-max-height, 90vh);
align-items: center;
transform: var(--dialog-transform);
transition: var(--dialog-transition);
}
wa-drawer::part(body) {
max-width: var(--ha-bottom-sheet-max-width);
width: 100%;
border-top-left-radius: var(
--ha-bottom-sheet-border-radius,
var(--ha-dialog-border-radius, var(--ha-border-radius-2xl))
);
border-top-right-radius: var(
--ha-bottom-sheet-border-radius,
var(--ha-dialog-border-radius, var(--ha-border-radius-2xl))
);
background-color: var(
--ha-bottom-sheet-surface-background,
var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff)),
);
padding: var(
--ha-bottom-sheet-padding,
0 var(--safe-area-inset-right) var(--safe-area-inset-bottom)
var(--safe-area-inset-left)
);
}
:host([flexcontent]) wa-drawer::part(body) {
display: flex;
flex-direction: column;
}
.content-wrapper {
position: relative;
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}
:host([flexcontent]) .body {
flex: 1;
max-width: 100%;
display: flex;
flex-direction: column;
padding: var(
--ha-bottom-sheet-padding,
0 var(--safe-area-inset-right) var(--safe-area-inset-bottom)
var(--safe-area-inset-left)
);
}
slot[name="footer"] {
display: block;
padding: var(--ha-space-0);
}
::slotted([slot="footer"]) {
display: flex;
padding: var(--ha-space-3) var(--ha-space-4) var(--ha-space-4)
var(--ha-space-4);
gap: var(--ha-space-3);
justify-content: flex-end;
align-items: center;
width: 100%;
box-sizing: border-box;
}
:host([flexcontent]) slot[name="footer"] {
flex-shrink: 0;
}
`,
];
}
}
declare global {

View File

@@ -32,6 +32,12 @@ export class HaGridSizeEditor extends LitElement {
@property({ attribute: false }) public step = 1;
@property({ type: Boolean, attribute: "rows-disabled" })
public rowsDisabled?: boolean;
@property({ type: Boolean, attribute: "columns-disabled" })
public columnsDisabled?: boolean;
@state() public _localValue?: CardGridSize = { rows: 1, columns: 1 };
protected willUpdate(changedProperties) {
@@ -42,9 +48,11 @@ export class HaGridSizeEditor extends LitElement {
protected render() {
const disabledColumns =
this.columnMin !== undefined && this.columnMin === this.columnMax;
this.columnsDisabled ||
(this.columnMin !== undefined && this.columnMin === this.columnMax);
const disabledRows =
this.rowMin !== undefined && this.rowMin === this.rowMax;
this.rowsDisabled ||
(this.rowMin !== undefined && this.rowMin === this.rowMax);
const autoHeight = this._localValue?.rows === "auto";
const fullWidth = this._localValue?.columns === "full";
@@ -72,7 +80,7 @@ export class HaGridSizeEditor extends LitElement {
@value-changed=${this._valueChanged}
@slider-moved=${this._sliderMoved}
.disabled=${disabledColumns}
tooltip-mode="always"
tooltip-mode=${disabledColumns ? "never" : "always"}
></ha-grid-layout-slider>
<ha-grid-layout-slider
@@ -88,7 +96,7 @@ export class HaGridSizeEditor extends LitElement {
@value-changed=${this._valueChanged}
@slider-moved=${this._sliderMoved}
.disabled=${disabledRows}
tooltip-mode="always"
tooltip-mode=${disabledRows ? "never" : "always"}
></ha-grid-layout-slider>
${!this.isDefault
? html`

View File

@@ -99,10 +99,7 @@ class HaMarkdownElement extends ReactiveElement {
}
);
render(
elements.map((e) => h(unsafeHTML(e))),
this.renderRoot
);
render(h(unsafeHTML(elements.join(""))), this.renderRoot);
this._resize();

View File

@@ -25,11 +25,11 @@ export class HaMarkdown extends LitElement {
@property({ type: Boolean }) public cache = false;
@query("ha-markdown-element") private _markdownElement!: ReactiveElement;
@query("ha-markdown-element") private _markdownElement?: ReactiveElement;
protected async getUpdateComplete() {
const result = await super.getUpdateComplete();
await this._markdownElement.updateComplete;
await this._markdownElement?.updateComplete;
return result;
}

View File

@@ -14,6 +14,7 @@ import memoizeOne from "memoize-one";
import { tinykeys } from "tinykeys";
import { fireEvent } from "../common/dom/fire_event";
import { caseInsensitiveStringCompare } from "../common/string/compare";
import { ScrollableFadeMixin } from "../mixins/scrollable-fade-mixin";
import { HaFuse } from "../resources/fuse";
import { haStyleScrollbar } from "../resources/styles";
import { loadVirtualizer } from "../resources/virtualizer";
@@ -59,7 +60,7 @@ export type PickerComboBoxSearchFn<T extends PickerComboBoxItem> = (
) => T[];
@customElement("ha-picker-combo-box")
export class HaPickerComboBox extends LitElement {
export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
@property({ attribute: false }) public hass?: HomeAssistant;
// eslint-disable-next-line lit/no-native-attributes
@@ -126,6 +127,10 @@ export class HaPickerComboBox extends LitElement {
@state() private _items: (PickerComboBoxItem | string)[] = [];
protected get scrollableElement(): HTMLElement | null {
return this._virtualizerElement as HTMLElement | null;
}
@state() private _sectionTitle?: string;
private _allItems: (PickerComboBoxItem | string)[] = [];
@@ -180,19 +185,22 @@ export class HaPickerComboBox extends LitElement {
</div>
`
: nothing}
<lit-virtualizer
.keyFunction=${this._keyFunction}
tabindex="0"
scroller
.items=${this._items}
.renderItem=${this._renderItem}
style="min-height: 36px;"
class=${this._listScrolled ? "scrolled" : ""}
@scroll=${this._onScrollList}
@focus=${this._focusList}
@visibilityChanged=${this._visibilityChanged}
>
</lit-virtualizer>`;
<div class="virtualizer-wrapper">
<lit-virtualizer
.keyFunction=${this._keyFunction}
tabindex="0"
scroller
.items=${this._items}
.renderItem=${this._renderItem}
style="min-height: 36px;"
class=${this._listScrolled ? "scrolled" : ""}
@scroll=${this._onScrollList}
@focus=${this._focusList}
@visibilityChanged=${this._visibilityChanged}
>
</lit-virtualizer>
${this.renderScrollableFades()}
</div>`;
}
private _renderSectionButtons() {
@@ -584,156 +592,167 @@ export class HaPickerComboBox extends LitElement {
private _keyFunction = (item: PickerComboBoxItem | string) =>
typeof item === "string" ? item : item.id;
static styles = [
haStyleScrollbar,
css`
:host {
display: flex;
flex-direction: column;
padding-top: var(--ha-space-3);
flex: 1;
}
static get styles() {
return [
...super.styles,
haStyleScrollbar,
css`
:host {
display: flex;
flex-direction: column;
padding-top: var(--ha-space-3);
flex: 1;
}
ha-textfield {
padding: 0 var(--ha-space-3);
margin-bottom: var(--ha-space-3);
}
ha-textfield {
padding: 0 var(--ha-space-3);
margin-bottom: var(--ha-space-3);
}
:host([mode="dialog"]) ha-textfield {
padding: 0 var(--ha-space-4);
}
:host([mode="dialog"]) ha-textfield {
padding: 0 var(--ha-space-4);
}
ha-combo-box-item {
width: 100%;
}
ha-combo-box-item {
width: 100%;
}
ha-combo-box-item.selected {
background-color: var(--ha-color-fill-neutral-quiet-hover);
}
@media (prefers-color-scheme: dark) {
ha-combo-box-item.selected {
background-color: var(--ha-color-fill-neutral-normal-hover);
background-color: var(--ha-color-fill-neutral-quiet-hover);
}
}
lit-virtualizer {
flex: 1;
}
@media (prefers-color-scheme: dark) {
ha-combo-box-item.selected {
background-color: var(--ha-color-fill-neutral-normal-hover);
}
}
lit-virtualizer:focus-visible {
outline: none;
}
.virtualizer-wrapper {
position: relative;
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}
lit-virtualizer.scrolled {
border-top: 1px solid var(--ha-color-border-neutral-quiet);
}
lit-virtualizer {
flex: 1;
}
.bottom-padding {
height: max(var(--safe-area-inset-bottom, 0px), var(--ha-space-8));
width: 100%;
}
lit-virtualizer:focus-visible {
outline: none;
}
.empty {
text-align: center;
}
lit-virtualizer.scrolled {
border-top: 1px solid var(--ha-color-border-neutral-quiet);
}
.combo-box-row {
display: flex;
width: 100%;
align-items: center;
box-sizing: border-box;
min-height: 36px;
}
.combo-box-row.current-value {
background-color: var(--ha-color-fill-primary-quiet-resting);
}
.bottom-padding {
height: max(var(--safe-area-inset-bottom, 0px), var(--ha-space-8));
width: 100%;
}
.combo-box-row.selected {
background-color: var(--ha-color-fill-neutral-quiet-hover);
}
.empty {
text-align: center;
}
.combo-box-row {
display: flex;
width: 100%;
align-items: center;
box-sizing: border-box;
min-height: 36px;
}
.combo-box-row.current-value {
background-color: var(--ha-color-fill-primary-quiet-resting);
}
@media (prefers-color-scheme: dark) {
.combo-box-row.selected {
background-color: var(--ha-color-fill-neutral-normal-hover);
background-color: var(--ha-color-fill-neutral-quiet-hover);
}
}
.sections {
display: flex;
flex-wrap: nowrap;
gap: var(--ha-space-2);
padding: var(--ha-space-3) var(--ha-space-3);
overflow: auto;
}
@media (prefers-color-scheme: dark) {
.combo-box-row.selected {
background-color: var(--ha-color-fill-neutral-normal-hover);
}
}
:host([mode="dialog"]) .sections {
padding: var(--ha-space-3) var(--ha-space-4);
}
.sections {
display: flex;
flex-wrap: nowrap;
gap: var(--ha-space-2);
padding: var(--ha-space-3) var(--ha-space-3);
overflow: auto;
}
.sections ha-filter-chip {
flex-shrink: 0;
--md-filter-chip-selected-container-color: var(
--ha-color-fill-primary-normal-hover
);
color: var(--primary-color);
}
:host([mode="dialog"]) .sections {
padding: var(--ha-space-3) var(--ha-space-4);
}
.sections .separator {
height: var(--ha-space-8);
width: 0;
border: 1px solid var(--ha-color-border-neutral-quiet);
}
.sections ha-filter-chip {
flex-shrink: 0;
--md-filter-chip-selected-container-color: var(
--ha-color-fill-primary-normal-hover
);
color: var(--primary-color);
}
.section-title,
.title {
background-color: var(--ha-color-fill-neutral-quiet-resting);
padding: var(--ha-space-1) var(--ha-space-2);
font-weight: var(--ha-font-weight-bold);
color: var(--secondary-text-color);
min-height: var(--ha-space-6);
display: flex;
align-items: center;
}
.sections .separator {
height: var(--ha-space-8);
width: 0;
border: 1px solid var(--ha-color-border-neutral-quiet);
}
.title {
width: 100%;
}
.section-title,
.title {
background-color: var(--ha-color-fill-neutral-quiet-resting);
padding: var(--ha-space-1) var(--ha-space-2);
font-weight: var(--ha-font-weight-bold);
color: var(--secondary-text-color);
min-height: var(--ha-space-6);
display: flex;
align-items: center;
}
:host([mode="dialog"]) .title {
padding: var(--ha-space-1) var(--ha-space-4);
}
.title {
width: 100%;
}
:host([mode="dialog"]) ha-textfield {
padding: 0 var(--ha-space-4);
}
:host([mode="dialog"]) .title {
padding: var(--ha-space-1) var(--ha-space-4);
}
.section-title-wrapper {
height: 0;
position: relative;
}
:host([mode="dialog"]) ha-textfield {
padding: 0 var(--ha-space-4);
}
.section-title {
opacity: 0;
position: absolute;
top: 1px;
width: calc(100% - var(--ha-space-8));
}
.section-title-wrapper {
height: 0;
position: relative;
}
.section-title.show {
opacity: 1;
z-index: 1;
}
.section-title {
opacity: 0;
position: absolute;
top: 1px;
width: calc(100% - var(--ha-space-8));
}
.empty-search {
display: flex;
width: 100%;
flex-direction: column;
align-items: center;
padding: var(--ha-space-3);
}
`,
];
.section-title.show {
opacity: 1;
z-index: 1;
}
.empty-search {
display: flex;
width: 100%;
flex-direction: column;
align-items: center;
padding: var(--ha-space-3);
}
`,
];
}
}
declare global {

View File

@@ -9,13 +9,13 @@ import { customElement, property, query, state } from "lit/decorators";
import { prepareZXingModule } from "barcode-detector";
import type QrScanner from "qr-scanner";
import { fireEvent } from "../common/dom/fire_event";
import { stopPropagation } from "../common/dom/stop_propagation";
import { addExternalBarCodeListener } from "../external_app/external_app_entrypoint";
import type { HomeAssistant } from "../types";
import "./ha-alert";
import "./ha-button";
import "./ha-dropdown";
import "./ha-dropdown-item";
import type { HaDropdownItem } from "./ha-dropdown-item";
import "./ha-button-menu";
import "./ha-list-item";
import "./ha-spinner";
import "./ha-textfield";
import type { HaTextField } from "./ha-textfield";
@@ -52,8 +52,6 @@ class HaQrScanner extends LitElement {
@state() private _warning?: string;
@state() private _selectedCamera?: string;
private _qrScanner?: QrScanner;
private _qrNotFoundCount = 0;
@@ -123,7 +121,7 @@ class HaQrScanner extends LitElement {
!this._error &&
this._cameras &&
this._cameras.length > 1
? html`<ha-dropdown @wa-select=${this._handleDropdownSelect}>
? html`<ha-button-menu fixed @closed=${stopPropagation}>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize(
@@ -133,17 +131,15 @@ class HaQrScanner extends LitElement {
></ha-icon-button>
${this._cameras!.map(
(camera) => html`
<ha-dropdown-item
<ha-list-item
.value=${camera.id}
class=${this._selectedCamera === camera.id
? "selected"
: ""}
@click=${this._cameraChanged}
>
${camera.label}
</ha-dropdown-item>
</ha-list-item>
`
)}
</ha-dropdown>`
</ha-button-menu>`
: nothing}
</div>`
: html`<ha-alert alert-type="warning">
@@ -209,9 +205,6 @@ class HaQrScanner extends LitElement {
private async _listCameras(qrScanner: typeof QrScanner): Promise<void> {
this._cameras = await qrScanner.listCameras(true);
if (this._cameras.length > 0) {
this._selectedCamera = this._cameras[0].id;
}
}
private _qrCodeError = (err: any) => {
@@ -259,12 +252,8 @@ class HaQrScanner extends LitElement {
this._qrCodeScanned(this._manualInput!.value);
}
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
const cameraId = ev.detail?.item?.value;
if (cameraId) {
this._selectedCamera = cameraId;
this._qrScanner?.setCamera(cameraId);
}
private _cameraChanged(ev: CustomEvent): void {
this._qrScanner?.setCamera((ev.target as any).value);
}
private _openExternalScanner() {
@@ -370,7 +359,7 @@ class HaQrScanner extends LitElement {
#canvas-container {
position: relative;
}
ha-icon-button {
ha-button-menu {
position: absolute;
bottom: 8px;
right: 8px;
@@ -380,9 +369,6 @@ class HaQrScanner extends LitElement {
color: white;
border-radius: var(--ha-border-radius-circle);
}
ha-dropdown-item.selected {
font-weight: var(--ha-font-weight-bold);
}
.row {
display: flex;
align-items: center;

File diff suppressed because one or more lines are too long

View File

@@ -10,6 +10,7 @@ import {
} from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { fireEvent } from "../common/dom/fire_event";
import { ScrollableFadeMixin } from "../mixins/scrollable-fade-mixin";
import { haStyleScrollbar } from "../resources/styles";
import type { HomeAssistant } from "../types";
import "./ha-dialog-header";
@@ -72,7 +73,7 @@ export type DialogWidth = "small" | "medium" | "large" | "full";
* @see https://github.com/home-assistant/frontend/issues/27143
*/
@customElement("ha-wa-dialog")
export class HaWaDialog extends LitElement {
export class HaWaDialog extends ScrollableFadeMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: "aria-labelledby" })
@@ -113,6 +114,10 @@ export class HaWaDialog extends LitElement {
@state()
private _bodyScrolled = false;
protected get scrollableElement(): HTMLElement | null {
return this.bodyContainer;
}
protected updated(
changedProperties: Map<string | number | symbol, unknown>
): void {
@@ -161,8 +166,11 @@ export class HaWaDialog extends LitElement {
<slot name="headerActionItems" slot="actionItems"></slot>
</ha-dialog-header>
</slot>
<div class="body ha-scrollbar" @scroll=${this._handleBodyScroll}>
<slot></slot>
<div class="content-wrapper">
<div class="body ha-scrollbar" @scroll=${this._handleBodyScroll}>
<slot></slot>
</div>
${this.renderScrollableFades()}
</div>
<slot name="footer" slot="footer"></slot>
</wa-dialog>
@@ -199,165 +207,180 @@ export class HaWaDialog extends LitElement {
this._bodyScrolled = (ev.target as HTMLDivElement).scrollTop > 0;
}
static styles = [
haStyleScrollbar,
css`
wa-dialog {
--full-width: var(--ha-dialog-width-full, min(95vw, var(--safe-width)));
--width: min(var(--ha-dialog-width-md, 580px), var(--full-width));
--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))
);
--wa-panel-border-radius: var(
--ha-dialog-border-radius,
var(--ha-border-radius-3xl)
);
max-width: var(--ha-dialog-max-width, var(--safe-width));
}
static get styles() {
return [
...super.styles,
haStyleScrollbar,
css`
wa-dialog {
--full-width: var(
--ha-dialog-width-full,
min(95vw, var(--safe-width))
);
--width: min(var(--ha-dialog-width-md, 580px), var(--full-width));
--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))
);
--wa-panel-border-radius: var(
--ha-dialog-border-radius,
var(--ha-border-radius-3xl)
);
max-width: var(--ha-dialog-max-width, var(--safe-width));
}
:host([width="small"]) wa-dialog {
--width: min(var(--ha-dialog-width-sm, 320px), var(--full-width));
}
:host([width="small"]) wa-dialog {
--width: min(var(--ha-dialog-width-sm, 320px), var(--full-width));
}
:host([width="large"]) wa-dialog {
--width: min(var(--ha-dialog-width-lg, 1024px), var(--full-width));
}
:host([width="large"]) wa-dialog {
--width: min(var(--ha-dialog-width-lg, 1024px), var(--full-width));
}
:host([width="full"]) wa-dialog {
--width: var(--full-width);
}
:host([width="full"]) wa-dialog {
--width: var(--full-width);
}
wa-dialog::part(dialog) {
min-width: var(--width, var(--full-width));
max-width: var(--width, var(--full-width));
max-height: var(
--ha-dialog-max-height,
calc(var(--safe-height) - var(--ha-space-20))
);
min-height: var(--ha-dialog-min-height);
margin-top: var(--dialog-surface-margin-top, auto);
/* Used to offset the dialog from the safe areas when space is limited */
transform: translate(
calc(
var(--safe-area-offset-left, var(--ha-space-0)) - var(
--safe-area-offset-right,
var(--ha-space-0)
)
),
calc(
var(--safe-area-offset-top, var(--ha-space-0)) - var(
--safe-area-offset-bottom,
var(--ha-space-0)
)
)
);
display: flex;
flex-direction: column;
overflow: hidden;
}
wa-dialog::part(dialog) {
min-width: var(--width, var(--full-width));
max-width: var(--width, var(--full-width));
max-height: var(
--ha-dialog-max-height,
calc(var(--safe-height) - var(--ha-space-20))
);
min-height: var(--ha-dialog-min-height);
margin-top: var(--dialog-surface-margin-top, auto);
/* Used to offset the dialog from the safe areas when space is limited */
transform: translate(
calc(
var(--safe-area-offset-left, var(--ha-space-0)) - var(
--safe-area-offset-right,
var(--ha-space-0)
)
),
calc(
var(--safe-area-offset-top, var(--ha-space-0)) - var(
--safe-area-offset-bottom,
var(--ha-space-0)
)
)
);
display: flex;
flex-direction: column;
overflow: hidden;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
:host([type="standard"]) {
--ha-dialog-border-radius: var(--ha-space-0);
@media all and (max-width: 450px), all and (max-height: 500px) {
:host([type="standard"]) {
--ha-dialog-border-radius: var(--ha-space-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);
}
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;
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;
}
}
}
}
.header-title-container {
display: flex;
align-items: center;
}
.header-title-container {
display: flex;
align-items: center;
}
.header-title {
margin: 0;
margin-bottom: 0;
color: var(--ha-dialog-header-title-color, var(--primary-text-color));
font-size: var(
--ha-dialog-header-title-font-size,
var(--ha-font-size-2xl)
);
line-height: var(
--ha-dialog-header-title-line-height,
var(--ha-line-height-condensed)
);
font-weight: var(
--ha-dialog-header-title-font-weight,
var(--ha-font-weight-normal)
);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: var(--ha-space-3);
}
.header-title {
margin: 0;
margin-bottom: 0;
color: var(--ha-dialog-header-title-color, var(--primary-text-color));
font-size: var(
--ha-dialog-header-title-font-size,
var(--ha-font-size-2xl)
);
line-height: var(
--ha-dialog-header-title-line-height,
var(--ha-line-height-condensed)
);
font-weight: var(
--ha-dialog-header-title-font-weight,
var(--ha-font-weight-normal)
);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: var(--ha-space-3);
}
wa-dialog::part(body) {
padding: 0;
display: flex;
flex-direction: column;
max-width: 100%;
overflow: hidden;
}
wa-dialog::part(body) {
padding: 0;
display: flex;
flex-direction: column;
max-width: 100%;
overflow: hidden;
}
.body {
position: var(--dialog-content-position, relative);
padding: 0 var(--dialog-content-padding, var(--ha-space-6))
var(--dialog-content-padding, var(--ha-space-6))
var(--dialog-content-padding, var(--ha-space-6));
overflow: auto;
flex-grow: 1;
}
:host([flexcontent]) .body {
max-width: 100%;
flex: 1;
display: flex;
flex-direction: column;
}
.content-wrapper {
position: relative;
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}
wa-dialog::part(footer) {
padding: var(--ha-space-0);
}
.body {
position: var(--dialog-content-position, relative);
padding: var(
--dialog-content-padding,
0 var(--ha-space-6) var(--ha-space-6) var(--ha-space-6)
);
overflow: auto;
flex-grow: 1;
}
:host([flexcontent]) .body {
max-width: 100%;
flex: 1;
display: flex;
flex-direction: column;
}
::slotted([slot="footer"]) {
display: flex;
padding: var(--ha-space-3) var(--ha-space-4) var(--ha-space-4)
var(--ha-space-4);
gap: var(--ha-space-3);
justify-content: flex-end;
align-items: center;
width: 100%;
}
`,
];
wa-dialog::part(footer) {
padding: var(--ha-space-0);
}
::slotted([slot="footer"]) {
display: flex;
padding: var(--ha-space-3) var(--ha-space-4) var(--ha-space-4)
var(--ha-space-4);
gap: var(--ha-space-3);
justify-content: flex-end;
align-items: center;
width: 100%;
}
`,
];
}
}
declare global {

View File

@@ -11,7 +11,7 @@ import {
isLastDayOfMonth,
addYears,
} from "date-fns";
import type { Collection } from "home-assistant-js-websocket";
import type { Collection, HassEntity } from "home-assistant-js-websocket";
import { getCollection } from "home-assistant-js-websocket";
import memoizeOne from "memoize-one";
import {
@@ -1361,3 +1361,37 @@ export const calculateSolarConsumedGauge = (
}
return undefined;
};
/**
* Get current power value from entity state, normalized to kW
* @param stateObj - The entity state object to get power value from
* @returns Power value in kW, or 0 if entity not found or invalid
*/
export const getPowerFromState = (stateObj: HassEntity): number | undefined => {
if (!stateObj) {
return undefined;
}
const value = parseFloat(stateObj.state);
if (isNaN(value)) {
return undefined;
}
// Normalize to kW based on unit of measurement (case-sensitive)
// Supported units: GW, kW, MW, mW, TW, W
const unit = stateObj.attributes.unit_of_measurement;
switch (unit) {
case "W":
return value / 1000;
case "mW":
return value / 1000000;
case "MW":
return value * 1000;
case "GW":
return value * 1000000;
case "TW":
return value * 1000000000;
default:
// Assume kW if no unit or unit is kW
return value;
}
};

View File

@@ -101,22 +101,18 @@ export const subscribeLabFeatures = (
* Subscribe to a specific lab feature
* @param conn - The connection to the Home Assistant instance
* @param domain - The domain of the lab feature
* @param previewFeature - The preview feature of the lab feature
* @param previewFeature - The preview feature identifier
* @param onChange - The function to call when the lab feature changes
* @returns The unsubscribe function
* @returns A promise that resolves to the unsubscribe function
*/
export const subscribeLabFeature = (
conn: Connection,
domain: string,
previewFeature: string,
onChange: (enabled: boolean) => void
) =>
subscribeLabFeatures(conn, (features) => {
const enabled =
features.find(
(feature) =>
feature.domain === domain &&
feature.preview_feature === previewFeature
)?.enabled ?? false;
onChange(enabled);
onChange: (feature: LabPreviewFeature) => void
): Promise<() => void> =>
conn.subscribeMessage<LabPreviewFeature>(onChange, {
type: "labs/subscribe",
domain,
preview_feature: previewFeature,
});

View File

@@ -220,12 +220,12 @@ const tryDescribeAction = <T extends ActionType>(
if (config.action) {
const [domain, serviceName] = config.action.split(".", 2);
const descriptionPlaceholders =
hass.services[domain][serviceName].description_placeholders;
hass.services[domain]?.[serviceName]?.description_placeholders;
const service =
hass.localize(
`component.${domain}.services.${serviceName}.name`,
descriptionPlaceholders
) || hass.services[domain][serviceName]?.name;
) || hass.services[domain]?.[serviceName]?.name;
if (config.metadata) {
return hass.localize(

View File

@@ -14,8 +14,9 @@ import {
import type { HassEntity } from "home-assistant-js-websocket";
import type { PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, property, query, state } from "lit/decorators";
import { cache } from "lit/directives/cache";
import { classMap } from "lit/directives/class-map";
import { keyed } from "lit/directives/keyed";
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../common/dom/fire_event";
@@ -50,7 +51,12 @@ import { lightSupportsFavoriteColors } from "../../data/light";
import type { ItemType } from "../../data/search";
import { SearchableDomains } from "../../data/search";
import { getSensorNumericDeviceClasses } from "../../data/sensor";
import { haStyleDialog, haStyleDialogFixedTop } from "../../resources/styles";
import { ScrollableFadeMixin } from "../../mixins/scrollable-fade-mixin";
import {
haStyleDialog,
haStyleDialogFixedTop,
haStyleScrollbar,
} from "../../resources/styles";
import "../../state-summary/state-card-content";
import type { HomeAssistant } from "../../types";
import {
@@ -96,13 +102,15 @@ declare global {
const DEFAULT_VIEW: View = "info";
@customElement("ha-more-info-dialog")
export class MoreInfoDialog extends LitElement {
export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean, reflect: true }) public large = false;
@state() private _parentEntityIds: string[] = [];
@query(".content") private _contentElement?: HTMLDivElement;
@state() private _entityId?: string | null;
@state() private _data?: Record<string, any>;
@@ -121,6 +129,12 @@ export class MoreInfoDialog extends LitElement {
@state() private _sensorNumericDeviceClasses?: string[] = [];
protected scrollFadeThreshold = 24;
protected get scrollableElement(): HTMLElement | null {
return this._contentElement || null;
}
public showDialog(params: MoreInfoDialogParams) {
this._entityId = params.entityId;
if (!this._entityId) {
@@ -302,7 +316,9 @@ export class MoreInfoDialog extends LitElement {
}
private _goToAddEntityTo(ev) {
if (!shouldHandleRequestSelectedEvent(ev)) return;
// Only check for request-selected events (from menu items), not regular clicks (from icon button)
if (ev.type === "request-selected" && !shouldHandleRequestSelectedEvent(ev))
return;
this._setView("add_to");
}
@@ -550,7 +566,18 @@ export class MoreInfoDialog extends LitElement {
: nothing}
</ha-button-menu>
`
: nothing}
: !__DEMO__ && this._shouldShowAddEntityTo()
? html`
<ha-icon-button
slot="actionItems"
.label=${this.hass.localize(
"ui.dialogs.more_info_control.add_entity_to"
)}
.path=${mdiPlusBoxMultipleOutline}
@click=${this._goToAddEntityTo}
></ha-icon-button>
`
: nothing}
`
: isSpecificInitialView
? html`
@@ -581,78 +608,86 @@ export class MoreInfoDialog extends LitElement {
`
: nothing}
</ha-dialog-header>
${keyed(
this._entityId,
html`
<div
class="content"
tabindex="-1"
dialogInitialFocus
@show-child-view=${this._showChildView}
@entity-entry-updated=${this._entryUpdated}
@toggle-edit-mode=${this._handleToggleInfoEditModeEvent}
@hass-more-info=${this._handleMoreInfoEvent}
>
${cache(
this._childView
? html`
<div class="child-view">
${dynamicElement(this._childView.viewTag, {
hass: this.hass,
entry: this._entry,
params: this._childView.viewParams,
})}
</div>
`
: this._currView === "info"
<div
class=${classMap({
"content-wrapper": true,
"settings-view": this._currView === "settings",
})}
>
${keyed(
this._entityId,
html`
<div
class="content ha-scrollbar"
tabindex="-1"
dialogInitialFocus
@show-child-view=${this._showChildView}
@entity-entry-updated=${this._entryUpdated}
@toggle-edit-mode=${this._handleToggleInfoEditModeEvent}
@hass-more-info=${this._handleMoreInfoEvent}
>
${cache(
this._childView
? html`
<ha-more-info-info
dialogInitialFocus
.hass=${this.hass}
.entityId=${this._entityId}
.entry=${this._entry}
.editMode=${this._infoEditMode}
.data=${this._data}
></ha-more-info-info>
<div class="child-view">
${dynamicElement(this._childView.viewTag, {
hass: this.hass,
entry: this._entry,
params: this._childView.viewParams,
})}
</div>
`
: this._currView === "history"
: this._currView === "info"
? html`
<ha-more-info-history-and-logbook
<ha-more-info-info
dialogInitialFocus
.hass=${this.hass}
.entityId=${this._entityId}
></ha-more-info-history-and-logbook>
.entry=${this._entry}
.editMode=${this._infoEditMode}
.data=${this._data}
></ha-more-info-info>
`
: this._currView === "settings"
: this._currView === "history"
? html`
<ha-more-info-settings
<ha-more-info-history-and-logbook
.hass=${this.hass}
.entityId=${this._entityId}
.entry=${this._entry}
></ha-more-info-settings>
></ha-more-info-history-and-logbook>
`
: this._currView === "related"
: this._currView === "settings"
? html`
<ha-related-items
<ha-more-info-settings
.hass=${this.hass}
.itemId=${entityId}
.itemType=${SearchableDomains.has(domain)
? (domain as ItemType)
: "entity"}
></ha-related-items>
.entityId=${this._entityId}
.entry=${this._entry}
></ha-more-info-settings>
`
: this._currView === "add_to"
: this._currView === "related"
? html`
<ha-more-info-add-to
<ha-related-items
.hass=${this.hass}
.entityId=${entityId}
@add-to-action-selected=${this._goBack}
></ha-more-info-add-to>
.itemId=${entityId}
.itemType=${SearchableDomains.has(domain)
? (domain as ItemType)
: "entity"}
></ha-related-items>
`
: nothing
)}
</div>
`
)}
: this._currView === "add_to"
? html`
<ha-more-info-add-to
.hass=${this.hass}
.entityId=${entityId}
@add-to-action-selected=${this._goBack}
></ha-more-info-add-to>
`
: nothing
)}
</div>
`
)}
${this.renderScrollableFades()}
</div>
</ha-dialog>
`;
}
@@ -707,18 +742,31 @@ export class MoreInfoDialog extends LitElement {
static get styles() {
return [
...super.styles,
haStyleDialog,
haStyleDialogFixedTop,
haStyleScrollbar,
css`
ha-dialog {
--dialog-content-padding: 0;
}
.content {
.content-wrapper {
flex: 1 1 auto;
min-height: 0;
position: relative;
display: flex;
flex-direction: column;
}
.content {
outline: none;
flex: 1;
overflow: auto;
}
.content-wrapper.settings-view .fade-bottom {
bottom: var(--ha-space-18);
}
.child-view {

View File

@@ -15,7 +15,7 @@ import "../../components/ha-alert";
import "../../components/ha-expansion-panel";
import "../../components/ha-fade-in";
import "../../components/ha-icon-next";
import "../../components/ha-wa-dialog";
import "../../components/ha-adaptive-dialog";
import "../../components/ha-md-list";
import "../../components/ha-md-list-item";
import "../../components/ha-spinner";
@@ -109,7 +109,7 @@ class DialogRestart extends LitElement {
const dialogTitle = this.hass.localize("ui.dialogs.restart.heading");
return html`
<ha-wa-dialog
<ha-adaptive-dialog
.hass=${this.hass}
.open=${this._dialogOpen}
header-title=${dialogTitle}
@@ -257,7 +257,7 @@ class DialogRestart extends LitElement {
</ha-expansion-panel>
`}
</div>
</ha-wa-dialog>
</ha-adaptive-dialog>
`;
}
@@ -405,7 +405,7 @@ class DialogRestart extends LitElement {
haStyle,
haStyleDialog,
css`
ha-wa-dialog {
ha-adaptive-dialog {
--dialog-content-padding: 0;
}

View File

@@ -1,11 +1,12 @@
import "@material/mwc-linear-progress/mwc-linear-progress";
import { mdiClose, mdiDotsVertical, mdiRestart } from "@mdi/js";
import { css, html, LitElement, nothing, type TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { mdiDotsVertical, mdiRestart } from "@mdi/js";
import { css, html, LitElement, type TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-alert";
import "../../components/ha-dialog-header";
import "../../components/ha-button";
import "../../components/ha-dialog-footer";
import "../../components/ha-fade-in";
import "../../components/ha-icon-button";
import "../../components/ha-items-display-editor";
@@ -14,9 +15,8 @@ import type {
DisplayValue,
} from "../../components/ha-items-display-editor";
import "../../components/ha-md-button-menu";
import "../../components/ha-md-dialog";
import type { HaMdDialog } from "../../components/ha-md-dialog";
import "../../components/ha-md-menu-item";
import "../../components/ha-wa-dialog";
import { computePanels } from "../../components/ha-sidebar";
import "../../components/ha-spinner";
import "../../components/ha-svg-icon";
@@ -40,8 +40,6 @@ class DialogEditSidebar extends LitElement {
@state() private _open = false;
@query("ha-md-dialog") private _dialog?: HaMdDialog;
@state() private _order?: string[];
@state() private _hidden?: string[];
@@ -55,7 +53,6 @@ class DialogEditSidebar extends LitElement {
public async showDialog(): Promise<void> {
this._open = true;
this._getData();
}
@@ -87,7 +84,7 @@ class DialogEditSidebar extends LitElement {
}
public closeDialog(): void {
this._dialog?.close();
this._open = false;
}
private _panels = memoizeOne((panels: HomeAssistant["panels"]) =>
@@ -167,57 +164,52 @@ class DialogEditSidebar extends LitElement {
}
protected render() {
if (!this._open) {
return nothing;
}
const dialogTitle = this.hass.localize("ui.sidebar.edit_sidebar");
return html`
<ha-md-dialog open @closed=${this._dialogClosed}>
<ha-dialog-header slot="headline">
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
header-title=${dialogTitle}
header-subtitle=${!this._migrateToUserData
? this.hass.localize("ui.sidebar.edit_subtitle")
: ""}
@closed=${this._dialogClosed}
>
<ha-md-button-menu
slot="headerActionItems"
positioning="popover"
anchor-corner="end-end"
menu-corner="start-end"
>
<ha-icon-button
slot="navigationIcon"
.label=${this.hass.localize("ui.common.close") ?? "Close"}
.path=${mdiClose}
@click=${this.closeDialog}
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<span slot="title" .title=${dialogTitle}>${dialogTitle}</span>
${!this._migrateToUserData
? html`<span slot="subtitle"
>${this.hass.localize("ui.sidebar.edit_subtitle")}</span
>`
: nothing}
<ha-md-button-menu
slot="actionItems"
positioning="popover"
anchor-corner="end-end"
menu-corner="start-end"
<ha-md-menu-item .clickAction=${this._resetToDefaults}>
<ha-svg-icon slot="start" .path=${mdiRestart}></ha-svg-icon>
${this.hass.localize("ui.sidebar.reset_to_defaults")}
</ha-md-menu-item>
</ha-md-button-menu>
<div class="content">${this._renderContent()}</div>
<ha-dialog-footer slot="footer">
<ha-button
slot="secondaryAction"
appearance="plain"
@click=${this.closeDialog}
>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<ha-md-menu-item .clickAction=${this._resetToDefaults}>
<ha-svg-icon slot="start" .path=${mdiRestart}></ha-svg-icon>
${this.hass.localize("ui.sidebar.reset_to_defaults")}
</ha-md-menu-item>
</ha-md-button-menu>
</ha-dialog-header>
<div slot="content" class="content">${this._renderContent()}</div>
<div slot="actions">
<ha-button appearance="plain" @click=${this.closeDialog}>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button
slot="primaryAction"
.disabled=${!this._order || !this._hidden}
@click=${this._save}
>
${this.hass.localize("ui.common.save")}
</ha-button>
</div>
</ha-md-dialog>
</ha-dialog-footer>
</ha-wa-dialog>
`;
}
@@ -272,15 +264,13 @@ class DialogEditSidebar extends LitElement {
}
static styles = css`
ha-md-dialog {
min-width: 600px;
ha-wa-dialog {
max-height: 90%;
--dialog-content-padding: 8px 24px;
--dialog-content-padding: var(--ha-space-2) var(--ha-space-6);
}
@media all and (max-width: 600px), all and (max-height: 500px) {
ha-md-dialog {
--md-dialog-container-shape: 0;
@media all and (max-width: 580px), all and (max-height: 500px) {
ha-wa-dialog {
min-width: 100%;
min-height: 100%;
}

View File

@@ -13,6 +13,7 @@ import "../components/ha-svg-icon";
import "../components/ha-tab";
import { haStyleScrollbar } from "../resources/styles";
import type { HomeAssistant, Route } from "../types";
import { withViewTransition } from "../common/util/view-transition";
export interface PageNavigation {
path: string;
@@ -112,9 +113,11 @@ class HassTabsSubpage extends LitElement {
public willUpdate(changedProperties: PropertyValues) {
if (changedProperties.has("route")) {
this._activeTab = this.tabs.find((tab) =>
`${this.route.prefix}${this.route.path}`.includes(tab.path)
);
withViewTransition(() => {
this._activeTab = this.tabs.find((tab) =>
`${this.route.prefix}${this.route.path}`.includes(tab.path)
);
});
}
super.willUpdate(changedProperties);
}

View File

@@ -13,19 +13,27 @@ import type { Constructor } from "../types";
const stylesArray = (styles?: CSSResultGroup | CSSResultGroup[]) =>
styles === undefined ? [] : Array.isArray(styles) ? styles : [styles];
/**
* Mixin that adds top and bottom fade overlays for scrollable content.
* @param superClass - The LitElement class to extend.
* @returns Extended class with scrollable fade functionality.
*/
export const ScrollableFadeMixin = <T extends Constructor<LitElement>>(
superClass: T
) => {
class ScrollableFadeClass extends superClass {
/** Whether content has scrolled past the threshold. Controls top fade visibility. */
@state() protected _contentScrolled = false;
/** Whether content extends beyond the viewport. Controls bottom fade visibility. */
@state() protected _contentScrollable = false;
private _scrollTarget?: HTMLElement | null;
private _onScroll = (ev: Event) => {
const target = ev.currentTarget as HTMLElement;
this._contentScrolled = (target.scrollTop ?? 0) > 0;
this._contentScrolled =
(target.scrollTop ?? 0) > this.scrollFadeThreshold;
this._updateScrollableState(target);
};
@@ -39,15 +47,26 @@ export const ScrollableFadeMixin = <T extends Constructor<LitElement>>(
},
});
private static readonly DEFAULT_SAFE_AREA_PADDING = 16;
/**
* Safe area padding in pixels for the scrollable element.
*/
protected scrollFadeSafeAreaPadding = 16;
/**
* Scroll threshold in pixels for showing the fades.
*/
protected scrollFadeThreshold = 4;
/**
* Default scrollable element value.
*/
private static readonly DEFAULT_SCROLLABLE_ELEMENT: HTMLElement | null =
null;
protected get scrollFadeSafeAreaPadding() {
return ScrollableFadeClass.DEFAULT_SAFE_AREA_PADDING;
}
/**
* Element to observe for scroll and resize events. Override with a getter to specify target.
* Kept as a getter to allow subclasses to return query results.
*/
protected get scrollableElement(): HTMLElement | null {
return ScrollableFadeClass.DEFAULT_SCROLLABLE_ELEMENT;
}
@@ -67,6 +86,11 @@ export const ScrollableFadeMixin = <T extends Constructor<LitElement>>(
super.disconnectedCallback();
}
/**
* Renders top and bottom fade overlays. Call in render method.
* @param rounded - Whether to apply rounded corners.
* @returns Template containing fade elements.
*/
protected renderScrollableFades(rounded = false): TemplateResult {
return html`
<div
@@ -110,7 +134,6 @@ export const ScrollableFadeMixin = <T extends Constructor<LitElement>>(
transparent
);
border-radius: var(--ha-border-radius-square);
z-index: 100;
opacity: 0;
}
.fade-top {

View File

@@ -64,8 +64,15 @@ export class HaStopAction extends LitElement implements ActionElement {
private _responseChanged(ev: Event) {
ev.stopPropagation();
const newAction = { ...this.action };
const newValue = (ev.target as any).value;
if (newValue) {
newAction.response_variable = newValue;
} else {
delete newAction.response_variable;
}
fireEvent(this, "value-changed", {
value: { ...this.action, response_variable: (ev.target as any).value },
value: newAction,
});
}

View File

@@ -228,7 +228,7 @@ class DialogAddAutomationElement
private _unsub?: Promise<UnsubscribeFunc>;
private _unsubscribeLabFeatures?: UnsubscribeFunc;
private _unsubscribeLabFeatures?: Promise<UnsubscribeFunc>;
private _configEntryLookup: Record<string, ConfigEntry> = {};
@@ -285,8 +285,8 @@ class DialogAddAutomationElement
this.hass.connection,
"automation",
"new_triggers_conditions",
(enabled) => {
this._newTriggersAndConditions = enabled;
(feature) => {
this._newTriggersAndConditions = feature.enabled;
this._tab = this._newTriggersAndConditions ? "targets" : "groups";
}
);
@@ -422,7 +422,7 @@ class DialogAddAutomationElement
this._unsub = undefined;
}
if (this._unsubscribeLabFeatures) {
this._unsubscribeLabFeatures();
this._unsubscribeLabFeatures.then((unsub) => unsub());
this._unsubscribeLabFeatures = undefined;
}
}

View File

@@ -94,8 +94,8 @@ export default class HaAutomationCondition extends SubscribeMixin(LitElement) {
this.hass!.connection,
"automation",
"new_triggers_conditions",
(enabled) => {
this._newTriggersAndConditions = enabled;
(feature) => {
this._newTriggersAndConditions = feature.enabled;
}
),
];

View File

@@ -98,9 +98,9 @@ export default class HaAutomationSidebarAction extends LitElement {
title = `${domainToName(this.hass.localize, domain)}: ${
this.hass.localize(
`component.${domain}.services.${service}.name`,
this.hass.services[domain][service].description_placeholders
this.hass.services[domain]?.[service]?.description_placeholders
) ||
this.hass.services[domain][service]?.name ||
this.hass.services[domain]?.[service]?.name ||
title
}`;
}

View File

@@ -89,8 +89,8 @@ export default class HaAutomationTrigger extends SubscribeMixin(LitElement) {
this.hass!.connection,
"automation",
"new_triggers_conditions",
(enabled) => {
this._newTriggersAndConditions = enabled;
(feature) => {
this._newTriggersAndConditions = feature.enabled;
}
),
];

View File

@@ -9,6 +9,7 @@ import "../../../../components/ha-dialog";
import "../../../../components/ha-formfield";
import "../../../../components/ha-radio";
import "../../../../components/ha-button";
import "../../../../components/ha-markdown";
import type { HaRadio } from "../../../../components/ha-radio";
import "../../../../components/ha-textfield";
import type { GasSourceTypeEnergyPreference } from "../../../../data/energy";
@@ -109,6 +110,15 @@ export class DialogEnergyGasSettings
? `${this.hass.config.currency}/${this._pickedDisplayUnit}`
: undefined;
const pickedUnitClass =
this._pickedDisplayUnit &&
this._energy_units?.includes(this._pickedDisplayUnit)
? "energy"
: this._pickedDisplayUnit &&
this._gas_units?.includes(this._pickedDisplayUnit)
? "volume"
: undefined;
const externalSource =
this._source.stat_energy_from &&
isExternalStatistic(this._source.stat_energy_from);
@@ -213,9 +223,33 @@ export class DialogEnergyGasSettings
.hass=${this.hass}
include-domains='["sensor", "input_number"]'
.value=${this._source.entity_energy_price}
.label=${`${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.config.energy.gas.dialog.cost_entity_input"
)} ${unitPrice ? ` (${unitPrice})` : ""}`}
)}
.helper=${pickedUnitClass
? html`<ha-markdown
.content=${this.hass.localize(
"ui.panel.config.energy.gas.dialog.cost_entity_helper",
pickedUnitClass === "energy"
? {
currency: this.hass.config.currency,
class: this.hass.localize(
"ui.panel.config.energy.gas.dialog.cost_entity_helper_energy"
),
unit1: "kWh",
unit2: "Wh",
}
: {
currency: this.hass.config.currency,
class: this.hass.localize(
"ui.panel.config.energy.gas.dialog.cost_entity_helper_volume"
),
unit1: "m³",
unit2: "ft³",
}
)}
></ha-markdown>`
: nothing}
@value-changed=${this._priceEntityChanged}
></ha-entity-picker>`
: ""}

View File

@@ -9,6 +9,7 @@ import "../../../../components/ha-dialog";
import "../../../../components/ha-button";
import "../../../../components/ha-formfield";
import "../../../../components/ha-radio";
import "../../../../components/ha-markdown";
import type { HaRadio } from "../../../../components/ha-radio";
import "../../../../components/ha-textfield";
import type { WaterSourceTypeEnergyPreference } from "../../../../data/energy";
@@ -16,11 +17,7 @@ import {
emptyWaterEnergyPreference,
energyStatisticHelpUrl,
} from "../../../../data/energy";
import {
getDisplayUnit,
getStatisticMetadata,
isExternalStatistic,
} from "../../../../data/recorder";
import { isExternalStatistic } from "../../../../data/recorder";
import { getSensorDeviceClassConvertibleUnits } from "../../../../data/sensor";
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
import { haStyle, haStyleDialog } from "../../../../resources/styles";
@@ -40,8 +37,6 @@ export class DialogEnergyWaterSettings
@state() private _costs?: "no-costs" | "number" | "entity" | "statistic";
@state() private _pickedDisplayUnit?: string | null;
@state() private _water_units?: string[];
@state() private _error?: string;
@@ -55,11 +50,6 @@ export class DialogEnergyWaterSettings
this._source = params.source
? { ...params.source }
: emptyWaterEnergyPreference();
this._pickedDisplayUnit = getDisplayUnit(
this.hass,
params.source?.stat_energy_from,
params.metadata
);
this._costs = this._source.entity_energy_price
? "entity"
: this._source.number_energy_price
@@ -79,7 +69,6 @@ export class DialogEnergyWaterSettings
this._params = undefined;
this._source = undefined;
this._error = undefined;
this._pickedDisplayUnit = undefined;
this._excludeList = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
return true;
@@ -92,10 +81,6 @@ export class DialogEnergyWaterSettings
const pickableUnit = this._water_units?.join(", ") || "";
const unitPriceSensor = this._pickedDisplayUnit
? `${this.hass.config.currency}/${this._pickedDisplayUnit}`
: undefined;
const unitPriceFixed = `${this.hass.config.currency}/${
this.hass.config.unit_system.volume === "gal" ? "gal" : "m³"
}`;
@@ -202,9 +187,15 @@ export class DialogEnergyWaterSettings
.hass=${this.hass}
include-domains='["sensor", "input_number"]'
.value=${this._source.entity_energy_price}
.label=${`${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.config.energy.water.dialog.cost_entity_input"
)}${unitPriceSensor ? ` (${unitPriceSensor})` : ""}`}
)}
.helper=${html`<ha-markdown
.content=${this.hass.localize(
"ui.panel.config.energy.water.dialog.cost_entity_helper",
{ currency: this.hass.config.currency }
)}
></ha-markdown>`}
@value-changed=${this._priceEntityChanged}
></ha-entity-picker>`
: ""}
@@ -287,16 +278,6 @@ export class DialogEnergyWaterSettings
}
private async _statisticChanged(ev: CustomEvent<{ value: string }>) {
if (ev.detail.value) {
const metadata = await getStatisticMetadata(this.hass, [ev.detail.value]);
this._pickedDisplayUnit = getDisplayUnit(
this.hass,
ev.detail.value,
metadata[0]
);
} else {
this._pickedDisplayUnit = undefined;
}
if (isExternalStatistic(ev.detail.value) && this._costs !== "statistic") {
this._costs = "no-costs";
}

View File

@@ -1525,17 +1525,17 @@ export class EntityRegistrySettingsEditor extends LitElement {
}
ha-textfield.entityId ha-icon-button {
position: relative;
right: -8px;
right: calc(var(--ha-space-2) * -1);
--mdc-icon-button-size: 36px;
--mdc-icon-size: 20px;
color: var(--secondary-text-color);
inset-inline-start: initial;
inset-inline-end: -8px;
inset-inline-end: calc(var(--ha-space-2) * -1);
direction: var(--direction);
}
ha-switch {
margin-right: 16px;
margin-inline-end: 16px;
margin-right: var(--ha-space-4);
margin-inline-end: var(--ha-space-4);
margin-inline-start: initial;
}
ha-settings-row ha-switch {
@@ -1548,7 +1548,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
ha-select,
ha-area-picker {
display: block;
margin: 8px 0;
margin: var(--ha-space-2) 0;
width: 100%;
}
li[divider] {

View File

@@ -244,14 +244,15 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
display: block;
}
.container {
padding: 8px 24px 20px 24px;
padding: var(--ha-space-2) var(--ha-space-6) var(--ha-space-5)
var(--ha-space-6);
}
.buttons {
box-sizing: border-box;
display: flex;
padding: 16px;
padding: var(--ha-space-4);
justify-content: space-between;
padding-bottom: max(var(--safe-area-inset-bottom), 16px);
padding-bottom: max(var(--safe-area-inset-bottom), var(--ha-space-4));
background-color: var(--mdc-theme-surface, #fff);
border-top: 1px solid var(--divider-color);
position: sticky;

View File

@@ -336,9 +336,7 @@ class HaConfigIntegrationsDashboard extends KeyboardShortcutMixin(
super.firstUpdated(changed);
this._fetchManifests();
this._fetchEntitySources();
if (this.route.path === "/add") {
this._handleAdd();
}
this._handleRouteChanged();
this._scanUSBDevices();
this._scanImprovDevices();
@@ -355,6 +353,9 @@ class HaConfigIntegrationsDashboard extends KeyboardShortcutMixin(
protected updated(changed: PropertyValues) {
super.updated(changed);
if (changed.has("route")) {
this._handleRouteChanged();
}
if (
(this._searchParms.has("config_entry") ||
this._searchParms.has("domain")) &&
@@ -813,10 +814,13 @@ class HaConfigIntegrationsDashboard extends KeyboardShortcutMixin(
}
}
private async _handleAdd() {
private async _handleRouteChanged() {
if (this.route?.path !== "/add") {
return;
}
const brand = extractSearchParam("brand");
const domain = extractSearchParam("domain");
navigate("/config/integrations", { replace: true });
navigate("/config/integrations/dashboard/", { replace: true });
if (brand) {
showAddIntegrationDialog(this, {

View File

@@ -1,11 +1,11 @@
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { relativeTime } from "../../../common/datetime/relative_time";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-button";
import type { HaMdDialog } from "../../../components/ha-md-dialog";
import "../../../components/ha-md-dialog";
import "../../../components/ha-dialog-footer";
import "../../../components/ha-wa-dialog";
import "../../../components/ha-md-list";
import "../../../components/ha-md-list-item";
import type { HaSwitch } from "../../../components/ha-switch";
@@ -30,13 +30,14 @@ export class DialogLabsPreviewFeatureEnable
@state() private _createBackup = false;
@query("ha-md-dialog") private _dialog?: HaMdDialog;
@state() private _open = false;
public async showDialog(
params: LabsPreviewFeatureEnableDialogParams
): Promise<void> {
this._params = params;
this._createBackup = false;
this._open = true;
this._fetchBackupConfig();
if (isComponentLoaded(this.hass, "hassio")) {
this._fetchUpdateBackupConfig();
@@ -44,7 +45,7 @@ export class DialogLabsPreviewFeatureEnable
}
public closeDialog(): boolean {
this._dialog?.close();
this._open = false;
return true;
}
@@ -143,72 +144,69 @@ export class DialogLabsPreviewFeatureEnable
const createBackupTexts = this._computeCreateBackupTexts();
return html`
<ha-md-dialog open @closed=${this._dialogClosed}>
<span slot="headline">
${this.hass.localize("ui.panel.config.labs.enable_title")}
</span>
<div slot="content">
<p>
${this.hass.localize(
`component.${this._params.preview_feature.domain}.preview_features.${this._params.preview_feature.preview_feature}.enable_confirmation`
) || this.hass.localize("ui.panel.config.labs.enable_confirmation")}
</p>
</div>
<div slot="actions">
${createBackupTexts
? html`
<ha-md-list>
<ha-md-list-item>
<span slot="headline">${createBackupTexts.title}</span>
${createBackupTexts.description
? html`
<span slot="supporting-text">
${createBackupTexts.description}
</span>
`
: nothing}
<ha-switch
slot="end"
.checked=${this._createBackup}
@change=${this._createBackupChanged}
></ha-switch>
</ha-md-list-item>
</ha-md-list>
`
: nothing}
<div>
<ha-button appearance="plain" @click=${this._handleCancel}>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button
appearance="filled"
variant="brand"
@click=${this._handleConfirm}
>
${this.hass.localize("ui.panel.config.labs.enable")}
</ha-button>
</div>
</div>
</ha-md-dialog>
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
header-title=${this.hass.localize("ui.panel.config.labs.enable_title")}
@closed=${this._dialogClosed}
>
<p>
${this.hass.localize(
`component.${this._params.preview_feature.domain}.preview_features.${this._params.preview_feature.preview_feature}.enable_confirmation`
) || this.hass.localize("ui.panel.config.labs.enable_confirmation")}
</p>
${createBackupTexts
? html`
<ha-md-list>
<ha-md-list-item>
<span slot="headline">${createBackupTexts.title}</span>
${createBackupTexts.description
? html`
<span slot="supporting-text">
${createBackupTexts.description}
</span>
`
: nothing}
<ha-switch
slot="end"
.checked=${this._createBackup}
@change=${this._createBackupChanged}
></ha-switch>
</ha-md-list-item>
</ha-md-list>
`
: nothing}
<ha-dialog-footer slot="footer">
<ha-button
slot="secondaryAction"
appearance="plain"
@click=${this._handleCancel}
>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button
slot="primaryAction"
appearance="filled"
variant="brand"
@click=${this._handleConfirm}
>
${this.hass.localize("ui.panel.config.labs.enable")}
</ha-button>
</ha-dialog-footer>
</ha-wa-dialog>
`;
}
static readonly styles = css`
ha-md-dialog {
--dialog-content-padding: var(--ha-space-6);
ha-wa-dialog {
--dialog-content-padding: var(--ha-space-0);
}
p {
margin: 0;
margin: 0 var(--ha-space-6) var(--ha-space-6);
color: var(--secondary-text-color);
}
div[slot="actions"] {
display: flex;
flex-direction: column;
padding: 0;
}
ha-md-list {
background: none;
--md-list-item-leading-space: var(--ha-space-6);
@@ -217,13 +215,6 @@ export class DialogLabsPreviewFeatureEnable
padding: 0;
border-top: var(--ha-border-width-sm) solid var(--divider-color);
}
div[slot="actions"] > div {
display: flex;
justify-content: flex-end;
gap: var(--ha-space-2);
padding: var(--ha-space-4) var(--ha-space-6);
}
`;
}

View File

@@ -1,7 +1,7 @@
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-md-dialog";
import "../../../components/ha-wa-dialog";
import "../../../components/ha-spinner";
import type { HassDialog } from "../../../dialogs/make-dialog-manager";
import type { HomeAssistant } from "../../../types";
@@ -39,36 +39,36 @@ export class DialogLabsProgress
}
return html`
<ha-md-dialog
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
disable-cancel-action
prevent-scrim-close
@closed=${this._handleClosed}
>
<div slot="content">
<div class="summary">
<ha-spinner></ha-spinner>
<div class="content">
<p class="heading">
${this.hass.localize(
"ui.panel.config.labs.progress.creating_backup"
)}
</p>
<p class="description">
${this.hass.localize(
this._params.enabled
? "ui.panel.config.labs.progress.backing_up_before_enabling"
: "ui.panel.config.labs.progress.backing_up_before_disabling"
)}
</p>
</div>
<div slot="header"></div>
<div class="summary">
<ha-spinner></ha-spinner>
<div class="content">
<p class="heading">
${this.hass.localize(
"ui.panel.config.labs.progress.creating_backup"
)}
</p>
<p class="description">
${this.hass.localize(
this._params.enabled
? "ui.panel.config.labs.progress.backing_up_before_enabling"
: "ui.panel.config.labs.progress.backing_up_before_disabling"
)}
</p>
</div>
</div>
</ha-md-dialog>
</ha-wa-dialog>
`;
}
static readonly styles = css`
ha-md-dialog {
ha-wa-dialog {
--dialog-content-padding: var(--ha-space-6);
}

View File

@@ -4,12 +4,10 @@ import { css, html, LitElement } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { navigate } from "../../../common/navigate";
import { stringCompare } from "../../../common/string/compare";
import { extractSearchParam } from "../../../common/url/search-params";
import "../../../components/ha-button";
import "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
import "../../../components/ha-button-menu";
import "../../../components/ha-list-item";
import "../../../components/search-input";
import type { LogProvider } from "../../../data/error_log";
import { fetchHassioAddonsInfo } from "../../../data/hassio/addon";
@@ -20,6 +18,7 @@ import type { HomeAssistant, Route } from "../../../types";
import "./error-log-card";
import "./system-log-card";
import type { SystemLogCard } from "./system-log-card";
import { stringCompare } from "../../../common/string/compare";
const logProviders: LogProvider[] = [
{
@@ -118,10 +117,7 @@ export class HaConfigLogs extends LitElement {
>
${isComponentLoaded(this.hass, "hassio")
? html`
<ha-dropdown
slot="toolbar-icon"
@wa-select=${this._handleDropdownSelect}
>
<ha-button-menu slot="toolbar-icon">
<ha-button slot="trigger" appearance="filled">
<ha-svg-icon slot="end" .path=${mdiChevronDown}></ha-svg-icon>
${this._logProviders.find(
@@ -130,17 +126,16 @@ export class HaConfigLogs extends LitElement {
</ha-button>
${this._logProviders.map(
(provider) => html`
<ha-dropdown-item
.value=${provider.key}
class=${provider.key === this._selectedLogProvider
? "selected"
: ""}
<ha-list-item
?selected=${provider.key === this._selectedLogProvider}
.provider=${provider.key}
@click=${this._selectProvider}
>
${provider.name}
</ha-dropdown-item>
</ha-list-item>
`
)}
</ha-dropdown>
</ha-button-menu>
`
: ""}
${search}
@@ -175,12 +170,8 @@ export class HaConfigLogs extends LitElement {
this._detail = !this._detail;
}
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
const provider = ev.detail?.item?.value;
if (!provider) {
return;
}
this._selectedLogProvider = provider;
private _selectProvider(ev) {
this._selectedLogProvider = (ev.currentTarget as any).provider;
this._filter = "";
navigate(`/config/logs?provider=${this._selectedLogProvider}`);
}
@@ -263,7 +254,7 @@ export class HaConfigLogs extends LitElement {
direction: ltr;
}
@media all and (max-width: 870px) {
ha-dropdown {
ha-button-menu {
max-width: 50%;
}
ha-button {
@@ -274,8 +265,8 @@ export class HaConfigLogs extends LitElement {
white-space: nowrap;
}
}
ha-dropdown-item.selected {
font-weight: var(--ha-font-weight-bold);
ha-list-item[selected] {
color: var(--primary-color);
}
`,
];

View File

@@ -4,6 +4,7 @@ import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { navigate } from "../../common/navigate";
import { withViewTransition } from "../../common/util/view-transition";
import "../../components/ha-button-menu";
import "../../components/ha-icon-button";
import "../../components/ha-list-item";
@@ -116,7 +117,9 @@ class PanelDeveloperTools extends LitElement {
return;
}
if (newPage !== this._page) {
navigate(`/developer-tools/${newPage}`);
withViewTransition(() => {
navigate(`/developer-tools/${newPage}`);
});
} else {
scrollTo({ behavior: "smooth", top: 0 });
}
@@ -125,7 +128,9 @@ class PanelDeveloperTools extends LitElement {
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
navigate(`/developer-tools/debug`);
withViewTransition(() => {
navigate(`/developer-tools/debug`);
});
break;
}
}

View File

@@ -27,6 +27,9 @@ export class EnergyOverviewViewStrategy extends ReactiveElement {
const energyCollection = getEnergyDataCollection(hass, {
key: collectionKey,
});
if (!energyCollection.prefs) {
await energyCollection.refresh();
}
const prefs = energyCollection.prefs;
// No energy sources available

View File

@@ -21,6 +21,9 @@ export class EnergyViewStrategy extends ReactiveElement {
const energyCollection = getEnergyDataCollection(hass, {
key: collectionKey,
});
if (!energyCollection.prefs) {
await energyCollection.refresh();
}
const prefs = energyCollection.prefs;
// No energy sources available

View File

@@ -24,6 +24,9 @@ export class GasViewStrategy extends ReactiveElement {
const energyCollection = getEnergyDataCollection(hass, {
key: collectionKey,
});
if (!energyCollection.prefs) {
await energyCollection.refresh();
}
const prefs = energyCollection.prefs;
const hasGasSources = prefs?.energy_sources.some(

View File

@@ -24,6 +24,9 @@ export class WaterViewStrategy extends ReactiveElement {
const energyCollection = getEnergyDataCollection(hass, {
key: collectionKey,
});
if (!energyCollection.prefs) {
await energyCollection.refresh();
}
const prefs = energyCollection.prefs;
const hasWaterSources = prefs?.energy_sources.some(

View File

@@ -6,7 +6,10 @@ import { classMap } from "lit/directives/class-map";
import "../../../../components/ha-card";
import "../../../../components/ha-svg-icon";
import type { EnergyData, EnergyPreferences } from "../../../../data/energy";
import { getEnergyDataCollection } from "../../../../data/energy";
import {
getEnergyDataCollection,
getPowerFromState,
} from "../../../../data/energy";
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../../../types";
import type { LovelaceCard, LovelaceGridOptions } from "../../types";
@@ -724,33 +727,7 @@ class HuiPowerSankeyCard
// Track this entity for state change detection
this._entities.add(entityId);
const stateObj = this.hass.states[entityId];
if (!stateObj) {
return 0;
}
const value = parseFloat(stateObj.state);
if (isNaN(value)) {
return 0;
}
// Normalize to kW based on unit of measurement (case-sensitive)
// Supported units: GW, kW, MW, mW, TW, W
const unit = stateObj.attributes.unit_of_measurement;
switch (unit) {
case "W":
return value / 1000;
case "mW":
return value / 1000000;
case "MW":
return value * 1000;
case "GW":
return value * 1000000;
case "TW":
return value * 1000000000;
default:
// Assume kW if no unit or unit is kW
return value;
}
return getPowerFromState(this.hass.states[entityId]) ?? 0;
}
/**

View File

@@ -10,7 +10,10 @@ import { LinearGradient } from "../../../../resources/echarts/echarts";
import "../../../../components/chart/ha-chart-base";
import "../../../../components/ha-card";
import type { EnergyData } from "../../../../data/energy";
import { getEnergyDataCollection } from "../../../../data/energy";
import {
getEnergyDataCollection,
getPowerFromState,
} from "../../../../data/energy";
import type { StatisticValue } from "../../../../data/recorder";
import type { FrontendLocaleData } from "../../../../data/translation";
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
@@ -197,6 +200,7 @@ export class HuiPowerSourcesGraphCard
},
};
const now = Date.now();
Object.keys(statIds).forEach((key, keyIndex) => {
if (statIds[key].stats.length) {
const colorHex = computedStyles.getPropertyValue(statIds[key].color);
@@ -204,7 +208,14 @@ export class HuiPowerSourcesGraphCard
// Echarts is supposed to handle that but it is bugged when you use it together with stacking.
// The interpolation breaks the stacking, so this positive/negative is a workaround
const { positive, negative } = this._processData(
statIds[key].stats.map((id: string) => energyData.stats[id] ?? [])
statIds[key].stats.map((id: string) => {
const stats = energyData.stats[id] ?? [];
const currentState = getPowerFromState(this.hass.states[id]);
if (currentState !== undefined) {
stats.push({ start: now, end: now, mean: currentState });
}
return stats;
})
);
datasets.push({
...commonSeriesOptions,

View File

@@ -80,12 +80,6 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard {
throw new Error("Entities need to be an array");
}
const computedStyles = getComputedStyle(this);
this._calendars = config!.entities.map((entity, idx) => ({
entity_id: entity,
backgroundColor: getColorByIndex(idx, computedStyles),
}));
if (this._config?.entities !== config.entities) {
this._fetchCalendarEvents();
}
@@ -93,6 +87,20 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard {
this._config = { initial_view: "dayGridMonth", ...config };
}
public willUpdate(changedProps: PropertyValues): void {
super.willUpdate(changedProps);
if (
!this.hasUpdated ||
(changedProps.has("_config") && this._config?.entities)
) {
const computedStyles = getComputedStyle(this);
this._calendars = this._config!.entities.map((entity, idx) => ({
entity_id: entity,
backgroundColor: getColorByIndex(idx, computedStyles),
}));
}
}
public getCardSize(): number {
return 12;
}

View File

@@ -20,6 +20,7 @@ import type {
import type {
LovelaceCard,
LovelaceCardEditor,
LovelaceGridOptions,
LovelaceHeaderFooter,
} from "../types";
import type { EntitiesCardConfig } from "./types";
@@ -139,6 +140,15 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
return size;
}
public getGridOptions(): LovelaceGridOptions {
return {
columns: 12,
rows: "auto",
min_columns: 3,
fixed_rows: true,
};
}
public setConfig(config: EntitiesCardConfig): void {
if (!config.entities || !Array.isArray(config.entities)) {
throw new Error("Entities must be specified");

View File

@@ -96,8 +96,6 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
`;
}
const entityState = Number(stateObj.state);
if (stateObj.state === UNAVAILABLE) {
return html`
<hui-warning
@@ -164,7 +162,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
.unit_of_measurement ||
""}
style=${styleMap({
"--gauge-color": this._computeSeverity(entityState),
"--gauge-color": this._computeSeverity(Number(valueToDisplay)),
})}
.needle=${this._config!.needle}
.levels=${this._config!.needle ? this._severityLevels() : undefined}

View File

@@ -94,7 +94,10 @@ class HuiMapCard extends LitElement implements LovelaceCard {
}
});
return locationEntities.filter((entity) => !personSources.has(entity));
return locationEntities.filter(
(entityId) =>
!hass.entities?.[entityId]?.hidden && !personSources.has(entityId)
);
}
public setConfig(config: MapCardConfig): void {

View File

@@ -3,7 +3,11 @@ import { property, state } from "lit/decorators";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCard, LovelaceCardEditor } from "../types";
import type {
LovelaceCard,
LovelaceCardEditor,
LovelaceGridOptions,
} from "../types";
import "./hui-card";
import type { HuiCard } from "./hui-card";
import type { StackCardConfig } from "./types";
@@ -39,6 +43,15 @@ export abstract class HuiStackCard<T extends StackCardConfig = StackCardConfig>
return 1;
}
public getGridOptions(): LovelaceGridOptions {
return {
columns: 12,
rows: "auto",
min_columns: 3,
fixed_rows: true,
};
}
public setConfig(config: T): void {
if (!config || !config.cards || !Array.isArray(config.cards)) {
throw new Error("Invalid configuration");

View File

@@ -457,13 +457,16 @@ export class HaGridLayoutSlider extends LitElement {
height: 4px;
width: 100%;
}
:host(:disabled) .slider {
:host([disabled]) .slider {
cursor: not-allowed;
}
:host(:disabled) .handle:after {
:host([disabled]) .track {
opacity: 0.5;
}
:host([disabled]) .handle::after {
background: var(--disabled-color);
}
:host(:disabled) .active {
:host([disabled]) .active {
background: var(--disabled-color);
}

View File

@@ -140,6 +140,8 @@ export class HuiCardLayoutEditor extends LitElement {
.rowMax=${gridOptions.max_rows}
.columnMin=${gridOptions.min_columns}
.columnMax=${gridOptions.max_columns}
.rowsDisabled=${this._defaultGridOptions?.fixed_rows}
.columnsDisabled=${this._defaultGridOptions?.fixed_columns}
.step=${this._preciseMode ? 1 : GRID_COLUMN_MULTIPLIER}
></ha-grid-size-picker>
<ha-settings-row>

View File

@@ -99,6 +99,7 @@ import type { HUIView } from "./views/hui-view";
import "./views/hui-view-background";
import "./views/hui-view-container";
import { UndoRedoController } from "../../common/controllers/undo-redo-controller";
import { withViewTransition } from "../../common/util/view-transition";
interface ActionItem {
icon: string;
@@ -655,8 +656,13 @@ class HUIRoot extends LitElement {
this.toggleAttribute("scrolled", window.scrollY !== 0);
};
private _locationChanged = () => {
this._handleUrlChanged();
};
private _handlePopState = () => {
this._restoreScroll = true;
this._handleUrlChanged();
};
private _isVisible = (view: LovelaceViewConfig) =>
@@ -678,6 +684,34 @@ class HUIRoot extends LitElement {
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
window.addEventListener("scroll", this._handleWindowScroll, {
passive: true,
});
this._handleUrlChanged();
}
public connectedCallback(): void {
super.connectedCallback();
window.addEventListener("scroll", this._handleWindowScroll, {
passive: true,
});
window.addEventListener("popstate", this._handlePopState);
window.addEventListener("location-changed", this._locationChanged);
// Disable history scroll restoration because it is managed manually here
window.history.scrollRestoration = "manual";
}
public disconnectedCallback(): void {
super.disconnectedCallback();
window.removeEventListener("scroll", this._handleWindowScroll);
window.removeEventListener("popstate", this._handlePopState);
window.removeEventListener("location-changed", this._locationChanged);
this.toggleAttribute("scrolled", window.scrollY !== 0);
// Re-enable history scroll restoration when leaving the page
window.history.scrollRestoration = "auto";
}
private _handleUrlChanged() {
// Check for requested edit mode
const searchParams = extractSearchParamsObject();
if (searchParams.edit === "1") {
@@ -697,29 +731,6 @@ class HUIRoot extends LitElement {
this._showMoreInfoDialog(entityId);
});
}
window.addEventListener("scroll", this._handleWindowScroll, {
passive: true,
});
}
public connectedCallback(): void {
super.connectedCallback();
window.addEventListener("scroll", this._handleWindowScroll, {
passive: true,
});
window.addEventListener("popstate", this._handlePopState);
// Disable history scroll restoration because it is managed manually here
window.history.scrollRestoration = "manual";
}
public disconnectedCallback(): void {
super.disconnectedCallback();
window.removeEventListener("scroll", this._handleWindowScroll);
window.removeEventListener("popstate", this._handlePopState);
this.toggleAttribute("scrolled", window.scrollY !== 0);
// Re-enable history scroll restoration when leaving the page
window.history.scrollRestoration = "auto";
}
protected willUpdate(changedProperties: PropertyValues): void {
@@ -1235,7 +1246,9 @@ class HUIRoot extends LitElement {
const viewIndex = Number(ev.detail.name);
if (viewIndex !== this._curView) {
const path = this.config.views[viewIndex].path || viewIndex;
this._navigateToView(path);
withViewTransition(() => {
this._navigateToView(path);
});
} else if (!this._editMode) {
scrollTo({ behavior: "smooth", top: 0 });
}

View File

@@ -3159,7 +3159,7 @@
"stat_predicted_production": "Prediction of your solar energy production",
"dialog": {
"header": "Configure solar panels",
"entity_para": "Pick a sensor which measures solar energy production in either of {unit}.",
"entity_para": "Pick a sensor which measures solar production in either of {unit}.",
"solar_production_energy": "Solar production energy",
"solar_production_power": "Solar production power",
"solar_production_forecast": "Solar production forecast",
@@ -3206,6 +3206,9 @@
"cost_stat_input": "[%key:ui::panel::config::energy::grid::flow_dialog::from::cost_stat_input%]",
"cost_entity": "[%key:ui::panel::config::energy::grid::flow_dialog::from::cost_entity%]",
"cost_entity_input": "[%key:ui::panel::config::energy::grid::flow_dialog::from::cost_entity_input%]",
"cost_entity_helper": "Any entity with a unit of `{currency}/(valid {class} unit)` (e.g. `{currency}/{unit1}` or `{currency}/{unit2}`) may be used and will be automatically converted.",
"cost_entity_helper_energy": "energy",
"cost_entity_helper_volume": "volume",
"cost_number": "[%key:ui::panel::config::energy::grid::flow_dialog::from::cost_number%]",
"cost_number_input": "[%key:ui::panel::config::energy::grid::flow_dialog::from::cost_number%]",
"gas_usage": "Gas usage"
@@ -3229,6 +3232,7 @@
"cost_stat_input": "[%key:ui::panel::config::energy::grid::flow_dialog::from::cost_stat_input%]",
"cost_entity": "[%key:ui::panel::config::energy::grid::flow_dialog::from::cost_entity%]",
"cost_entity_input": "[%key:ui::panel::config::energy::grid::flow_dialog::from::cost_entity_input%]",
"cost_entity_helper": "Any entity with a unit of `{currency}/(valid water unit)` (e.g. `{currency}/gal` or `{currency}/m³`) may be used and will be automatically converted.",
"cost_number": "[%key:ui::panel::config::energy::grid::flow_dialog::from::cost_number%]",
"cost_number_input": "[%key:ui::panel::config::energy::grid::flow_dialog::from::cost_number%]",
"water_usage": "Water usage"

550
yarn.lock
View File

@@ -3312,58 +3312,58 @@ __metadata:
languageName: node
linkType: hard
"@module-federation/error-codes@npm:0.21.4":
version: 0.21.4
resolution: "@module-federation/error-codes@npm:0.21.4"
checksum: 10/18b0ecdba0de4cd5b202f1f5e5aa76273de0e4513ba91b8e39a2a5b00ff6471f694c88d591624b0913a0c3b4c9a6112da29dc7eebbdc562e5455b591f1d70b31
"@module-federation/error-codes@npm:0.21.6":
version: 0.21.6
resolution: "@module-federation/error-codes@npm:0.21.6"
checksum: 10/6ded1ecab780f1f9ec46a59adb200e75cdf11580d70aa79dd75d71fbbf276615690da277ea67aa1ceb5bc88386f5708cc1d2ba5526be5c9ff02397a6123e36bf
languageName: node
linkType: hard
"@module-federation/runtime-core@npm:0.21.4":
version: 0.21.4
resolution: "@module-federation/runtime-core@npm:0.21.4"
"@module-federation/runtime-core@npm:0.21.6":
version: 0.21.6
resolution: "@module-federation/runtime-core@npm:0.21.6"
dependencies:
"@module-federation/error-codes": "npm:0.21.4"
"@module-federation/sdk": "npm:0.21.4"
checksum: 10/b90fe4147cd3302ea6b02098ff2bed7437425609ba0eb4d7b649b3643837810239f06f532af092a53f9b47e8fae1aeef84cfd964431fa4ab2fd4a800ad796d23
"@module-federation/error-codes": "npm:0.21.6"
"@module-federation/sdk": "npm:0.21.6"
checksum: 10/85efa2042d6f3a7cf0e4971b991472d4339d88f6f15684afb6d451f19ed934e225b2510c86b7bb4d2c5f64253ed7b0175f08c17f95bfc2b9929930a8a03fff1e
languageName: node
linkType: hard
"@module-federation/runtime-tools@npm:0.21.4":
version: 0.21.4
resolution: "@module-federation/runtime-tools@npm:0.21.4"
"@module-federation/runtime-tools@npm:0.21.6":
version: 0.21.6
resolution: "@module-federation/runtime-tools@npm:0.21.6"
dependencies:
"@module-federation/runtime": "npm:0.21.4"
"@module-federation/webpack-bundler-runtime": "npm:0.21.4"
checksum: 10/1e453268122070e5512c1d74cb8b4efb87cd2c1b46daba1736dfee16b2e8332a779f8168dcb3f84e17eade31f965168df63dae487ccfb74b0469c32af1895675
"@module-federation/runtime": "npm:0.21.6"
"@module-federation/webpack-bundler-runtime": "npm:0.21.6"
checksum: 10/36e7ccab948e11f310e87397a1a2185b56064e5691e553b34173686e2bc7372ec710e5ad48c026eb28c85b168765788b743aa2111513f3b57118b47636312dd1
languageName: node
linkType: hard
"@module-federation/runtime@npm:0.21.4":
version: 0.21.4
resolution: "@module-federation/runtime@npm:0.21.4"
"@module-federation/runtime@npm:0.21.6":
version: 0.21.6
resolution: "@module-federation/runtime@npm:0.21.6"
dependencies:
"@module-federation/error-codes": "npm:0.21.4"
"@module-federation/runtime-core": "npm:0.21.4"
"@module-federation/sdk": "npm:0.21.4"
checksum: 10/ae262bfe1643a381e571d7dff459108da3046eea04cc3dae85dce745dd294ef8a30f70098ffa602c266b8c5878c859dd2fcde787773303dac35f77fa6ed32ae4
"@module-federation/error-codes": "npm:0.21.6"
"@module-federation/runtime-core": "npm:0.21.6"
"@module-federation/sdk": "npm:0.21.6"
checksum: 10/93fd9bb284630933cab7e4bc070d648b56272f3636038c05eec7d1e3eeb189be3ccebe5f8ecc450197ee992d2616ed282d54e673ec0acd63adee4faddf80b144
languageName: node
linkType: hard
"@module-federation/sdk@npm:0.21.4":
version: 0.21.4
resolution: "@module-federation/sdk@npm:0.21.4"
checksum: 10/74c9ee2a057babf4f2638f8644a6eee6bd2c76441440dcc3855fb01a0e527e88518b8cc9c2d6d8f6b28858e34e40a3a966c03bb5d42897b9ea9163985edfa159
"@module-federation/sdk@npm:0.21.6":
version: 0.21.6
resolution: "@module-federation/sdk@npm:0.21.6"
checksum: 10/effc4aa932e2f06742bda8f02aaec84e138f5512b50f18c38b051490020b20d3d8edf7ece853fccffc1f78a0b43dec78e69bf02150e7e2801d5ce03c3ee367b9
languageName: node
linkType: hard
"@module-federation/webpack-bundler-runtime@npm:0.21.4":
version: 0.21.4
resolution: "@module-federation/webpack-bundler-runtime@npm:0.21.4"
"@module-federation/webpack-bundler-runtime@npm:0.21.6":
version: 0.21.6
resolution: "@module-federation/webpack-bundler-runtime@npm:0.21.6"
dependencies:
"@module-federation/runtime": "npm:0.21.4"
"@module-federation/sdk": "npm:0.21.4"
checksum: 10/a4f2a7ca7765651023af88f38ded9580b553cd8c6a88bc9056ec4dc58656a3d438f0498750462f424bf3aeaa5a3c7b6fd8189b7c7c76d084736474455838cb55
"@module-federation/runtime": "npm:0.21.6"
"@module-federation/sdk": "npm:0.21.6"
checksum: 10/a5ceb72ee3867acad5d7d3c654eb568068b1d5288f60ce9301bdc9f56effa5a4c26a732a2cec7176a81b87139566cd51dd8dfbc6112da05d47b870fa3ad3ba1f
languageName: node
linkType: hard
@@ -3893,22 +3893,22 @@ __metadata:
languageName: node
linkType: hard
"@rsdoctor/client@npm:1.3.11":
version: 1.3.11
resolution: "@rsdoctor/client@npm:1.3.11"
checksum: 10/17b769e8b6ae23e508816be05fb5b9ab235bd4380f2bc3ff7a8add8ae088eee0663627a637d695f304841fb708f19b245ab20a4924f3b0b9f35e341649028490
"@rsdoctor/client@npm:1.3.12":
version: 1.3.12
resolution: "@rsdoctor/client@npm:1.3.12"
checksum: 10/3abd14af2bd9a34da2199bad3a9c9aef34381beebebf3d0aceb90e4e9dc9ea19e804372bcdb8e082888dedb268fe544b658537c510df7ae9160df822ced9712f
languageName: node
linkType: hard
"@rsdoctor/core@npm:1.3.11":
version: 1.3.11
resolution: "@rsdoctor/core@npm:1.3.11"
"@rsdoctor/core@npm:1.3.12":
version: 1.3.12
resolution: "@rsdoctor/core@npm:1.3.12"
dependencies:
"@rsbuild/plugin-check-syntax": "npm:1.5.0"
"@rsdoctor/graph": "npm:1.3.11"
"@rsdoctor/sdk": "npm:1.3.11"
"@rsdoctor/types": "npm:1.3.11"
"@rsdoctor/utils": "npm:1.3.11"
"@rsdoctor/graph": "npm:1.3.12"
"@rsdoctor/sdk": "npm:1.3.12"
"@rsdoctor/types": "npm:1.3.12"
"@rsdoctor/utils": "npm:1.3.12"
browserslist-load-config: "npm:^1.0.1"
enhanced-resolve: "npm:5.12.0"
es-toolkit: "npm:^1.41.0"
@@ -3916,59 +3916,59 @@ __metadata:
fs-extra: "npm:^11.1.1"
semver: "npm:^7.7.3"
source-map: "npm:^0.7.6"
checksum: 10/1e11f76f00ef4148743c5b5ef8dcefdc57f658ffefcf6de6455888b4546000ebf6a9d03623734bbb7e3b5cd619bb460c668c38e6ffa0f747890018bbc3fc005a
checksum: 10/fda3d1a0acb0e57888a5bbe28649c1b439951e915d6411982116bdaabb72185d4f339009fd748681fe1c9c341c3c46fad597b587d8c3273b2024d682f6b40aca
languageName: node
linkType: hard
"@rsdoctor/graph@npm:1.3.11":
version: 1.3.11
resolution: "@rsdoctor/graph@npm:1.3.11"
"@rsdoctor/graph@npm:1.3.12":
version: 1.3.12
resolution: "@rsdoctor/graph@npm:1.3.12"
dependencies:
"@rsdoctor/types": "npm:1.3.11"
"@rsdoctor/utils": "npm:1.3.11"
"@rsdoctor/types": "npm:1.3.12"
"@rsdoctor/utils": "npm:1.3.12"
es-toolkit: "npm:^1.41.0"
path-browserify: "npm:1.0.1"
source-map: "npm:^0.7.6"
checksum: 10/e32444685f98cad184eb9d07bc9ebd4a3668c6dfaa352ce6912cabc4cdc63bdf15efc57a3d145b35e14724215230bd6657b7b8de30aa2c674a64c2c7983a3739
checksum: 10/38b07882c0e90fc9a97c68ee571f7b649c454624d403e8745ef85b6c2ad2c698f922aa1fb313a23cc1ceadfe391f48b23d381f73cf089ab5762a14a095dd26f3
languageName: node
linkType: hard
"@rsdoctor/rspack-plugin@npm:1.3.11":
version: 1.3.11
resolution: "@rsdoctor/rspack-plugin@npm:1.3.11"
"@rsdoctor/rspack-plugin@npm:1.3.12":
version: 1.3.12
resolution: "@rsdoctor/rspack-plugin@npm:1.3.12"
dependencies:
"@rsdoctor/core": "npm:1.3.11"
"@rsdoctor/graph": "npm:1.3.11"
"@rsdoctor/sdk": "npm:1.3.11"
"@rsdoctor/types": "npm:1.3.11"
"@rsdoctor/utils": "npm:1.3.11"
"@rsdoctor/core": "npm:1.3.12"
"@rsdoctor/graph": "npm:1.3.12"
"@rsdoctor/sdk": "npm:1.3.12"
"@rsdoctor/types": "npm:1.3.12"
"@rsdoctor/utils": "npm:1.3.12"
peerDependencies:
"@rspack/core": "*"
peerDependenciesMeta:
"@rspack/core":
optional: true
checksum: 10/973626b9387d85814839c7a8f08b21b60da079c4f755dd5ada5fae8aba61910d27ead333c35d3f9154ba71253c420bed43481bd68c09dae618d42f47a6d8c080
checksum: 10/2825034e62d89d9e9eeb5d4fa68ab3b19a12ee6b270fb103fd694e649dcaccfe08016879271c5d2cd2ee961179df43e9b124f965d2933b24dfaadc98441c2cc3
languageName: node
linkType: hard
"@rsdoctor/sdk@npm:1.3.11":
version: 1.3.11
resolution: "@rsdoctor/sdk@npm:1.3.11"
"@rsdoctor/sdk@npm:1.3.12":
version: 1.3.12
resolution: "@rsdoctor/sdk@npm:1.3.12"
dependencies:
"@rsdoctor/client": "npm:1.3.11"
"@rsdoctor/graph": "npm:1.3.11"
"@rsdoctor/types": "npm:1.3.11"
"@rsdoctor/utils": "npm:1.3.11"
"@rsdoctor/client": "npm:1.3.12"
"@rsdoctor/graph": "npm:1.3.12"
"@rsdoctor/types": "npm:1.3.12"
"@rsdoctor/utils": "npm:1.3.12"
safer-buffer: "npm:2.1.2"
socket.io: "npm:4.8.1"
tapable: "npm:2.2.3"
checksum: 10/6e98b51259178af26cad8852a2ac4f35a404a34978d60262f6100320b02c47fdcd11df2080c384d765eca28edfed65c8f0e1b49bf6cdac462d5c0cff0ec599f9
checksum: 10/5b60197500b73b1ae671565a2c2886a40df36fcf0e7277457840f569cb845d2c725fbfe43a4f6496ac5a26d749e0249e1a2a79bdb582a0fe16beeaa9bea0cdaa
languageName: node
linkType: hard
"@rsdoctor/types@npm:1.3.11":
version: 1.3.11
resolution: "@rsdoctor/types@npm:1.3.11"
"@rsdoctor/types@npm:1.3.12":
version: 1.3.12
resolution: "@rsdoctor/types@npm:1.3.12"
dependencies:
"@types/connect": "npm:3.4.38"
"@types/estree": "npm:1.0.5"
@@ -3982,16 +3982,16 @@ __metadata:
optional: true
webpack:
optional: true
checksum: 10/383cc2182737d6a9ab49a43f826bc16c5a2688f1135fe44586b835153c24fcae1bac5ea16683d252df91ec269608aa8e8926c07b0ed17f3d954e7a17d30a4650
checksum: 10/1d8891362056332289dfefd22a9ab0daf977ef8d82cffa1bc2abf507423ddf628242188b826bcf34e8fd908bb1b22f093c840675e77593a7f93e671e91261c5f
languageName: node
linkType: hard
"@rsdoctor/utils@npm:1.3.11":
version: 1.3.11
resolution: "@rsdoctor/utils@npm:1.3.11"
"@rsdoctor/utils@npm:1.3.12":
version: 1.3.12
resolution: "@rsdoctor/utils@npm:1.3.12"
dependencies:
"@babel/code-frame": "npm:7.26.2"
"@rsdoctor/types": "npm:1.3.11"
"@rsdoctor/types": "npm:1.3.12"
"@types/estree": "npm:1.0.5"
acorn: "npm:^8.10.0"
acorn-import-attributes: "npm:^1.9.5"
@@ -4005,96 +4005,96 @@ __metadata:
picocolors: "npm:^1.1.1"
rslog: "npm:^1.2.11"
strip-ansi: "npm:^6.0.1"
checksum: 10/f5d1bc0dbfc39753adbe2ee89d524a97d7568c6e1ad8059e479993fdb79e4b753e6cfb714bf641e5f336071215cb76c9b965a2ea004944d6c137e60d8285a1e7
checksum: 10/24d46e84abf2f85514f9feabb44db9b7a1e9e161052c8cdb8edc561db797928fd0d257249e1fb228f2b9346dc446e0ec59e03d46307f5b1e4bf5554a73274ee6
languageName: node
linkType: hard
"@rspack/binding-darwin-arm64@npm:1.6.5":
version: 1.6.5
resolution: "@rspack/binding-darwin-arm64@npm:1.6.5"
"@rspack/binding-darwin-arm64@npm:1.6.6":
version: 1.6.6
resolution: "@rspack/binding-darwin-arm64@npm:1.6.6"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@rspack/binding-darwin-x64@npm:1.6.5":
version: 1.6.5
resolution: "@rspack/binding-darwin-x64@npm:1.6.5"
"@rspack/binding-darwin-x64@npm:1.6.6":
version: 1.6.6
resolution: "@rspack/binding-darwin-x64@npm:1.6.6"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@rspack/binding-linux-arm64-gnu@npm:1.6.5":
version: 1.6.5
resolution: "@rspack/binding-linux-arm64-gnu@npm:1.6.5"
"@rspack/binding-linux-arm64-gnu@npm:1.6.6":
version: 1.6.6
resolution: "@rspack/binding-linux-arm64-gnu@npm:1.6.6"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
"@rspack/binding-linux-arm64-musl@npm:1.6.5":
version: 1.6.5
resolution: "@rspack/binding-linux-arm64-musl@npm:1.6.5"
"@rspack/binding-linux-arm64-musl@npm:1.6.6":
version: 1.6.6
resolution: "@rspack/binding-linux-arm64-musl@npm:1.6.6"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
"@rspack/binding-linux-x64-gnu@npm:1.6.5":
version: 1.6.5
resolution: "@rspack/binding-linux-x64-gnu@npm:1.6.5"
"@rspack/binding-linux-x64-gnu@npm:1.6.6":
version: 1.6.6
resolution: "@rspack/binding-linux-x64-gnu@npm:1.6.6"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
"@rspack/binding-linux-x64-musl@npm:1.6.5":
version: 1.6.5
resolution: "@rspack/binding-linux-x64-musl@npm:1.6.5"
"@rspack/binding-linux-x64-musl@npm:1.6.6":
version: 1.6.6
resolution: "@rspack/binding-linux-x64-musl@npm:1.6.6"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
"@rspack/binding-wasm32-wasi@npm:1.6.5":
version: 1.6.5
resolution: "@rspack/binding-wasm32-wasi@npm:1.6.5"
"@rspack/binding-wasm32-wasi@npm:1.6.6":
version: 1.6.6
resolution: "@rspack/binding-wasm32-wasi@npm:1.6.6"
dependencies:
"@napi-rs/wasm-runtime": "npm:1.0.7"
conditions: cpu=wasm32
languageName: node
linkType: hard
"@rspack/binding-win32-arm64-msvc@npm:1.6.5":
version: 1.6.5
resolution: "@rspack/binding-win32-arm64-msvc@npm:1.6.5"
"@rspack/binding-win32-arm64-msvc@npm:1.6.6":
version: 1.6.6
resolution: "@rspack/binding-win32-arm64-msvc@npm:1.6.6"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@rspack/binding-win32-ia32-msvc@npm:1.6.5":
version: 1.6.5
resolution: "@rspack/binding-win32-ia32-msvc@npm:1.6.5"
"@rspack/binding-win32-ia32-msvc@npm:1.6.6":
version: 1.6.6
resolution: "@rspack/binding-win32-ia32-msvc@npm:1.6.6"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@rspack/binding-win32-x64-msvc@npm:1.6.5":
version: 1.6.5
resolution: "@rspack/binding-win32-x64-msvc@npm:1.6.5"
"@rspack/binding-win32-x64-msvc@npm:1.6.6":
version: 1.6.6
resolution: "@rspack/binding-win32-x64-msvc@npm:1.6.6"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@rspack/binding@npm:1.6.5":
version: 1.6.5
resolution: "@rspack/binding@npm:1.6.5"
"@rspack/binding@npm:1.6.6":
version: 1.6.6
resolution: "@rspack/binding@npm:1.6.6"
dependencies:
"@rspack/binding-darwin-arm64": "npm:1.6.5"
"@rspack/binding-darwin-x64": "npm:1.6.5"
"@rspack/binding-linux-arm64-gnu": "npm:1.6.5"
"@rspack/binding-linux-arm64-musl": "npm:1.6.5"
"@rspack/binding-linux-x64-gnu": "npm:1.6.5"
"@rspack/binding-linux-x64-musl": "npm:1.6.5"
"@rspack/binding-wasm32-wasi": "npm:1.6.5"
"@rspack/binding-win32-arm64-msvc": "npm:1.6.5"
"@rspack/binding-win32-ia32-msvc": "npm:1.6.5"
"@rspack/binding-win32-x64-msvc": "npm:1.6.5"
"@rspack/binding-darwin-arm64": "npm:1.6.6"
"@rspack/binding-darwin-x64": "npm:1.6.6"
"@rspack/binding-linux-arm64-gnu": "npm:1.6.6"
"@rspack/binding-linux-arm64-musl": "npm:1.6.6"
"@rspack/binding-linux-x64-gnu": "npm:1.6.6"
"@rspack/binding-linux-x64-musl": "npm:1.6.6"
"@rspack/binding-wasm32-wasi": "npm:1.6.6"
"@rspack/binding-win32-arm64-msvc": "npm:1.6.6"
"@rspack/binding-win32-ia32-msvc": "npm:1.6.6"
"@rspack/binding-win32-x64-msvc": "npm:1.6.6"
dependenciesMeta:
"@rspack/binding-darwin-arm64":
optional: true
@@ -4116,23 +4116,23 @@ __metadata:
optional: true
"@rspack/binding-win32-x64-msvc":
optional: true
checksum: 10/1a2c9ef1865e92f36615ff997b336c42dca84584e487d43b739a2485108463db860f798e4a7a400a5b6a6e3ce1d7ba7c0fe01fcb55e11c153be6b521264c284e
checksum: 10/37b69398a0679c25e0479b6eb11ea2c110a8b57367af2c808a473d19d58c9dd09e7763b3dfbec06284d6863e7a301d71509128fe22da2b0c57c06b718f67e66a
languageName: node
linkType: hard
"@rspack/core@npm:1.6.5":
version: 1.6.5
resolution: "@rspack/core@npm:1.6.5"
"@rspack/core@npm:1.6.6":
version: 1.6.6
resolution: "@rspack/core@npm:1.6.6"
dependencies:
"@module-federation/runtime-tools": "npm:0.21.4"
"@rspack/binding": "npm:1.6.5"
"@module-federation/runtime-tools": "npm:0.21.6"
"@rspack/binding": "npm:1.6.6"
"@rspack/lite-tapable": "npm:1.1.0"
peerDependencies:
"@swc/helpers": ">=0.5.1"
peerDependenciesMeta:
"@swc/helpers":
optional: true
checksum: 10/4dc7b25b8b0535ce0dad8e7dfc8d8d4b031c3b626d93110e927a012f9564644f771b22838bc0b94f2e30a6408c6ddaf183e0c159e2bfa997b823688ad21c3a5c
checksum: 10/e706c19085729f52f3e80c6945edc32def2091270fc9a15d6aea336754719d0bdce93e62dae3a675c412b5e433cc2fc093591d74e731722196d0de2ff269b198
languageName: node
linkType: hard
@@ -4945,106 +4945,106 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:8.48.0":
version: 8.48.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.48.0"
"@typescript-eslint/eslint-plugin@npm:8.48.1":
version: 8.48.1
resolution: "@typescript-eslint/eslint-plugin@npm:8.48.1"
dependencies:
"@eslint-community/regexpp": "npm:^4.10.0"
"@typescript-eslint/scope-manager": "npm:8.48.0"
"@typescript-eslint/type-utils": "npm:8.48.0"
"@typescript-eslint/utils": "npm:8.48.0"
"@typescript-eslint/visitor-keys": "npm:8.48.0"
"@typescript-eslint/scope-manager": "npm:8.48.1"
"@typescript-eslint/type-utils": "npm:8.48.1"
"@typescript-eslint/utils": "npm:8.48.1"
"@typescript-eslint/visitor-keys": "npm:8.48.1"
graphemer: "npm:^1.4.0"
ignore: "npm:^7.0.0"
natural-compare: "npm:^1.4.0"
ts-api-utils: "npm:^2.1.0"
peerDependencies:
"@typescript-eslint/parser": ^8.48.0
"@typescript-eslint/parser": ^8.48.1
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/c9cd87c72da7bb7f6175fdb53a4c08a26e61a3d9d1024960d193276217b37ca1e8e12328a57751ed9380475e11e198f9715e172126ea7d3b3da9948d225db92b
checksum: 10/3ccf420805fb8adb2f3059fa26eb9c6211c0624966d8c8654a1bd586bf87f30be0c62524dfd785185ef573bedd91c42ec3c98c23aed5d60cb9ac583dd9334bc8
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:8.48.0":
version: 8.48.0
resolution: "@typescript-eslint/parser@npm:8.48.0"
"@typescript-eslint/parser@npm:8.48.1":
version: 8.48.1
resolution: "@typescript-eslint/parser@npm:8.48.1"
dependencies:
"@typescript-eslint/scope-manager": "npm:8.48.0"
"@typescript-eslint/types": "npm:8.48.0"
"@typescript-eslint/typescript-estree": "npm:8.48.0"
"@typescript-eslint/visitor-keys": "npm:8.48.0"
"@typescript-eslint/scope-manager": "npm:8.48.1"
"@typescript-eslint/types": "npm:8.48.1"
"@typescript-eslint/typescript-estree": "npm:8.48.1"
"@typescript-eslint/visitor-keys": "npm:8.48.1"
debug: "npm:^4.3.4"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/5919642345c79a43e57a85e0e69d1f56b5756b3fdb3586ec6371969604f589adc188338c8f12a787456edc3b38c70586d8209cffcf45e35e5a5ebd497c5f4257
checksum: 10/d8409c9ede4b1cd2ad0e10e94bb00c54f79352f7d54c97bf24419cb983c19b9f6097e6c31b217ce7ec5cfc9a48117e732d9f88ce0cb8c0ccf7fc3faecdf854a3
languageName: node
linkType: hard
"@typescript-eslint/project-service@npm:8.48.0":
version: 8.48.0
resolution: "@typescript-eslint/project-service@npm:8.48.0"
"@typescript-eslint/project-service@npm:8.48.1":
version: 8.48.1
resolution: "@typescript-eslint/project-service@npm:8.48.1"
dependencies:
"@typescript-eslint/tsconfig-utils": "npm:^8.48.0"
"@typescript-eslint/types": "npm:^8.48.0"
"@typescript-eslint/tsconfig-utils": "npm:^8.48.1"
"@typescript-eslint/types": "npm:^8.48.1"
debug: "npm:^4.3.4"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10/5853a2f57bf8a26b70c1fe5a906c1890ad4f0fca127218a7805161fc9ad547af97f4a600f32f5acdf2f2312b156affca2bea84af9a433215cbcc2056b6a27c77
checksum: 10/66ecc7ef9572748860517cde7fbfc335d05ca8c99dcf13ac6d728ac93388d90cdc3ebe2ff33a85c0a03487b3c1c4e36c6e3fe413ee16d8fb003621cb58e65e52
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:8.48.0":
version: 8.48.0
resolution: "@typescript-eslint/scope-manager@npm:8.48.0"
"@typescript-eslint/scope-manager@npm:8.48.1":
version: 8.48.1
resolution: "@typescript-eslint/scope-manager@npm:8.48.1"
dependencies:
"@typescript-eslint/types": "npm:8.48.0"
"@typescript-eslint/visitor-keys": "npm:8.48.0"
checksum: 10/963af7af235e940467504969c565b359ca454a156eba0d5af2e4fd9cca4294947187e1a85107ff05801688ac85b5767d2566414cbef47a03c23f7b46527decca
"@typescript-eslint/types": "npm:8.48.1"
"@typescript-eslint/visitor-keys": "npm:8.48.1"
checksum: 10/5040246220f9872ec47633297b7896ed5587af3163e06ddcb7ca0dcf1e171f359bd4f1c82f794a6adfecbccfb5ef437d51b522321034603c93ba1993c407bdf2
languageName: node
linkType: hard
"@typescript-eslint/tsconfig-utils@npm:8.48.0, @typescript-eslint/tsconfig-utils@npm:^8.48.0":
version: 8.48.0
resolution: "@typescript-eslint/tsconfig-utils@npm:8.48.0"
"@typescript-eslint/tsconfig-utils@npm:8.48.1, @typescript-eslint/tsconfig-utils@npm:^8.48.1":
version: 8.48.1
resolution: "@typescript-eslint/tsconfig-utils@npm:8.48.1"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10/e480cd80498c4119a8c5bc413a22abf4bf365b3674ff95f5513292ede31e4fd8118f50d76a786de702696396a43c0c7a4d0c2ccd1c2c7db61bd941ba74495021
checksum: 10/830bcd0e7628441f91899e8e24aaed66d32a239babcc205aba1d08c08ff5a636d8c04f96d9873578df59d7468fc4c5df032667764b3b2ee0a733af36fca21c4a
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:8.48.0":
version: 8.48.0
resolution: "@typescript-eslint/type-utils@npm:8.48.0"
"@typescript-eslint/type-utils@npm:8.48.1":
version: 8.48.1
resolution: "@typescript-eslint/type-utils@npm:8.48.1"
dependencies:
"@typescript-eslint/types": "npm:8.48.0"
"@typescript-eslint/typescript-estree": "npm:8.48.0"
"@typescript-eslint/utils": "npm:8.48.0"
"@typescript-eslint/types": "npm:8.48.1"
"@typescript-eslint/typescript-estree": "npm:8.48.1"
"@typescript-eslint/utils": "npm:8.48.1"
debug: "npm:^4.3.4"
ts-api-utils: "npm:^2.1.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/dfda42624d534f9fed270bd5c76c9c0bb879cccd3dfbfc2977c84489860fbc204f10bca5c69f3ac856cc4342c12f8947293e7449d3391af289620d7ec79ced0d
checksum: 10/6cf9370ac5437e2d64c71964646aed9e6c1ea3c7bb473258b50ae422106461d290f4215b9435b892a2dd563e3c31feb3169532375513b56b7e48f4a425283091
languageName: node
linkType: hard
"@typescript-eslint/types@npm:8.48.0, @typescript-eslint/types@npm:^8.48.0":
version: 8.48.0
resolution: "@typescript-eslint/types@npm:8.48.0"
checksum: 10/cd14a7ecd1cb6af94e059a713357b9521ffab08b2793a7d33abda7006816e77f634d49d1ec6f1b99b47257a605347d691bd02b2b11477c9c328f2a27f52a664f
"@typescript-eslint/types@npm:8.48.1, @typescript-eslint/types@npm:^8.48.1":
version: 8.48.1
resolution: "@typescript-eslint/types@npm:8.48.1"
checksum: 10/1aa1e3f25b429bcebd9eb45b5252d950f1b24dbc6014a47dff8d00547e2e1ac47f351846fb996b6ebd49da37a85394051d36191cbbbf2c431b8db9d95afd198d
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:8.48.0":
version: 8.48.0
resolution: "@typescript-eslint/typescript-estree@npm:8.48.0"
"@typescript-eslint/typescript-estree@npm:8.48.1":
version: 8.48.1
resolution: "@typescript-eslint/typescript-estree@npm:8.48.1"
dependencies:
"@typescript-eslint/project-service": "npm:8.48.0"
"@typescript-eslint/tsconfig-utils": "npm:8.48.0"
"@typescript-eslint/types": "npm:8.48.0"
"@typescript-eslint/visitor-keys": "npm:8.48.0"
"@typescript-eslint/project-service": "npm:8.48.1"
"@typescript-eslint/tsconfig-utils": "npm:8.48.1"
"@typescript-eslint/types": "npm:8.48.1"
"@typescript-eslint/visitor-keys": "npm:8.48.1"
debug: "npm:^4.3.4"
minimatch: "npm:^9.0.4"
semver: "npm:^7.6.0"
@@ -5052,32 +5052,32 @@ __metadata:
ts-api-utils: "npm:^2.1.0"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10/8ee6b9e98dd72d567b8842a695578b2098bd8cdcf5628d2819407a52b533a5a139ba9a5620976641bc4553144a1b971d75f2df218a7c281fe674df25835e9e22
checksum: 10/485aa44d22453396dbe61c560c6f583bf876f971d9e70773093cd729279f88184cf5793bf706033bbd8465cce6f9d045b63574727d58d5996519c29e1adbbfe5
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:8.48.0":
version: 8.48.0
resolution: "@typescript-eslint/utils@npm:8.48.0"
"@typescript-eslint/utils@npm:8.48.1":
version: 8.48.1
resolution: "@typescript-eslint/utils@npm:8.48.1"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.7.0"
"@typescript-eslint/scope-manager": "npm:8.48.0"
"@typescript-eslint/types": "npm:8.48.0"
"@typescript-eslint/typescript-estree": "npm:8.48.0"
"@typescript-eslint/scope-manager": "npm:8.48.1"
"@typescript-eslint/types": "npm:8.48.1"
"@typescript-eslint/typescript-estree": "npm:8.48.1"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/980b9faeaae0357bd7c002b15ab3bbcb7d5e4558be5df7980cf5221b41570a1a7b7d71ea2fcc8b1387f6c0db948d01468e6dcb31230d6757e28ac2ee5d8be4cf
checksum: 10/34afe5cf78020b682473e6529d6268eb8015bdb020a3c5303c4abb230d4d7c39e6fc8b9df58d1f0f35a1ceeb5d6182e71e42fe7a28dde8ffc31f8560f2dacc7c
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:8.48.0":
version: 8.48.0
resolution: "@typescript-eslint/visitor-keys@npm:8.48.0"
"@typescript-eslint/visitor-keys@npm:8.48.1":
version: 8.48.1
resolution: "@typescript-eslint/visitor-keys@npm:8.48.1"
dependencies:
"@typescript-eslint/types": "npm:8.48.0"
"@typescript-eslint/types": "npm:8.48.1"
eslint-visitor-keys: "npm:^4.2.1"
checksum: 10/f9eaff8225b3b00e486e0221bd596b08a3ed463f31fab88221256908f6208c48f745281b7b92e6358d25e1dbdc37c6c2f4b42503403c24b071165bafd9a35d52
checksum: 10/63aa165c57e6b38700adf84da2e90537577cdeb69d05031e3e70785fa412d96d539dc4c1696a0b7bc93284613f8b92fb1bb40f6068bb75347a942120b246ac60
languageName: node
linkType: hard
@@ -5367,12 +5367,12 @@ __metadata:
languageName: node
linkType: hard
"@vitest/coverage-v8@npm:4.0.14":
version: 4.0.14
resolution: "@vitest/coverage-v8@npm:4.0.14"
"@vitest/coverage-v8@npm:4.0.15":
version: 4.0.15
resolution: "@vitest/coverage-v8@npm:4.0.15"
dependencies:
"@bcoe/v8-coverage": "npm:^1.0.2"
"@vitest/utils": "npm:4.0.14"
"@vitest/utils": "npm:4.0.15"
ast-v8-to-istanbul: "npm:^0.3.8"
istanbul-lib-coverage: "npm:^3.2.2"
istanbul-lib-report: "npm:^3.0.1"
@@ -5383,34 +5383,34 @@ __metadata:
std-env: "npm:^3.10.0"
tinyrainbow: "npm:^3.0.3"
peerDependencies:
"@vitest/browser": 4.0.14
vitest: 4.0.14
"@vitest/browser": 4.0.15
vitest: 4.0.15
peerDependenciesMeta:
"@vitest/browser":
optional: true
checksum: 10/9d6c6a65f223603d2b881664d9e47da6b0b610a84753358195728118be63b6ec977234f707000490cc41bfd80390d8ad4b5af58d9d74b8f302233421cea20d43
checksum: 10/cdf5d26ba7f6f3895f72662549298e216f810a6cfce8a337d81d8b738df62f0766e0bb5c74f44b09d1282d4a83e14ac63e65c95cef461ac066f4b348c228f9a6
languageName: node
linkType: hard
"@vitest/expect@npm:4.0.14":
version: 4.0.14
resolution: "@vitest/expect@npm:4.0.14"
"@vitest/expect@npm:4.0.15":
version: 4.0.15
resolution: "@vitest/expect@npm:4.0.15"
dependencies:
"@standard-schema/spec": "npm:^1.0.0"
"@types/chai": "npm:^5.2.2"
"@vitest/spy": "npm:4.0.14"
"@vitest/utils": "npm:4.0.14"
"@vitest/spy": "npm:4.0.15"
"@vitest/utils": "npm:4.0.15"
chai: "npm:^6.2.1"
tinyrainbow: "npm:^3.0.3"
checksum: 10/7b8d8d1c8532358427a43a77e889131b038dca71021b208a15ec980b27acee7864a555cb3c58885a0302c179bf3c06698055c9fe89b476d477909c083cc0d9ea
checksum: 10/cfb1822012a7ba66d46224c94d2951a780668729199a81eed918103d74110333bd1296e8f598cf2345bac0998f01a71803146da97c8bb69d2775abf3918f02c9
languageName: node
linkType: hard
"@vitest/mocker@npm:4.0.14":
version: 4.0.14
resolution: "@vitest/mocker@npm:4.0.14"
"@vitest/mocker@npm:4.0.15":
version: 4.0.15
resolution: "@vitest/mocker@npm:4.0.15"
dependencies:
"@vitest/spy": "npm:4.0.14"
"@vitest/spy": "npm:4.0.15"
estree-walker: "npm:^3.0.3"
magic-string: "npm:^0.30.21"
peerDependencies:
@@ -5421,54 +5421,54 @@ __metadata:
optional: true
vite:
optional: true
checksum: 10/2a5c1507007803972cc6b23e4e560479c13bd90d54b597ac4586a3fec836d417dea533901bfcaf36bb3ca725d9f0ad419c03a77116912efc33f16ba7a170f126
checksum: 10/9f2aed963bd1bbe13f8acb5d05a95e3cf09d50e57708fb9e88cb4f18b0c0c9c854290bdffd8900914b64796ebdec4c068634487ec2fe55e7984941fff404601a
languageName: node
linkType: hard
"@vitest/pretty-format@npm:4.0.14":
version: 4.0.14
resolution: "@vitest/pretty-format@npm:4.0.14"
"@vitest/pretty-format@npm:4.0.15":
version: 4.0.15
resolution: "@vitest/pretty-format@npm:4.0.15"
dependencies:
tinyrainbow: "npm:^3.0.3"
checksum: 10/c5e896640dc4fedfec9e9a7f088fcfeadee3697d11bd7e1f15af7c7d25251a6e3c4590e9f7a24dc67f558c70a7962771f1d1621e2e88dcb02852382dfd96c42f
checksum: 10/c8ef240027ac340ae420a9b3eb77683a6399edd066832e27793eae19c189e567c5a225c1f26848aa2a2b7545dcc0c9019d6ff0a643cbf0eae004a05117fc2b05
languageName: node
linkType: hard
"@vitest/runner@npm:4.0.14":
version: 4.0.14
resolution: "@vitest/runner@npm:4.0.14"
"@vitest/runner@npm:4.0.15":
version: 4.0.15
resolution: "@vitest/runner@npm:4.0.15"
dependencies:
"@vitest/utils": "npm:4.0.14"
"@vitest/utils": "npm:4.0.15"
pathe: "npm:^2.0.3"
checksum: 10/9eb30c8dd84d6c2ae3fcc7bf7b0bb4ade85838cb8568148f15532d7e42aaee4ae43e1ff0d6d4dbece9dad593367b162785b0b3900e003e655070e01a4af04c0b
checksum: 10/682c070d00d0505bc4568e807a746238fe726290bcaea2695a009016ce2c396f8a3c090e5ed12795c1b65bcab4188d2fd8c513ce8324abf978272f319e445d19
languageName: node
linkType: hard
"@vitest/snapshot@npm:4.0.14":
version: 4.0.14
resolution: "@vitest/snapshot@npm:4.0.14"
"@vitest/snapshot@npm:4.0.15":
version: 4.0.15
resolution: "@vitest/snapshot@npm:4.0.15"
dependencies:
"@vitest/pretty-format": "npm:4.0.14"
"@vitest/pretty-format": "npm:4.0.15"
magic-string: "npm:^0.30.21"
pathe: "npm:^2.0.3"
checksum: 10/e5b7663652b605aeb9058591480ca5f32d1842d9372fa6c09b3a870c569953e04c510b26ae3eca31adebe9f8eef79c51ae2008432c776bbd2ba62c492e76ef9e
checksum: 10/f881257fc1c520541131296f9762d627ad61eb167a3d7129942a5c2dce46e870af1a8446fbf94d2fcdc5a31ab787ffff113f2b8dbd75b15d0494fe43db649682
languageName: node
linkType: hard
"@vitest/spy@npm:4.0.14":
version: 4.0.14
resolution: "@vitest/spy@npm:4.0.14"
checksum: 10/ab7dcef9c01456260c0f45e87d5250db2ce2480ce38412a8422df3c23c9f976ec35496d1593a5d3954552f3b737dbaea66bbddebb42c60621f98439638a296e1
"@vitest/spy@npm:4.0.15":
version: 4.0.15
resolution: "@vitest/spy@npm:4.0.15"
checksum: 10/700b06beb4fd33c1430bc5061e7c3055df9ad1e64500a0a02edba6a52e37ba3bf800eadfda1f617e1eeca53d7ab6941a69ba2812980347fcc3c3b736c5ae5a56
languageName: node
linkType: hard
"@vitest/utils@npm:4.0.14":
version: 4.0.14
resolution: "@vitest/utils@npm:4.0.14"
"@vitest/utils@npm:4.0.15":
version: 4.0.15
resolution: "@vitest/utils@npm:4.0.15"
dependencies:
"@vitest/pretty-format": "npm:4.0.14"
"@vitest/pretty-format": "npm:4.0.15"
tinyrainbow: "npm:^3.0.3"
checksum: 10/5cd2fee6e5115fc84297c092a8a35b10f962b0c92f5c64669a3523795b2a649b008642fc3c00844be8e6d52abd2b84527c5a618ef156f0a3222004ec2bb0f3e5
checksum: 10/54d3fd272e05ad43913d842a25dce705eb71db8591511f28fa4a6d0c28fd5eb109c580072e9f8dbc0f431425c890b74494c9d0b14f78d0be18ab87071f06d020
languageName: node
linkType: hard
@@ -9250,8 +9250,8 @@ __metadata:
"@octokit/plugin-retry": "npm:8.0.3"
"@octokit/rest": "npm:22.0.1"
"@replit/codemirror-indentation-markers": "npm:6.5.3"
"@rsdoctor/rspack-plugin": "npm:1.3.11"
"@rspack/core": "npm:1.6.5"
"@rsdoctor/rspack-plugin": "npm:1.3.12"
"@rspack/core": "npm:1.6.6"
"@rspack/dev-server": "npm:1.1.4"
"@swc/helpers": "npm:0.5.17"
"@thomasloven/round-slider": "npm:0.6.0"
@@ -9278,7 +9278,7 @@ __metadata:
"@vaadin/combo-box": "npm:24.9.6"
"@vaadin/vaadin-themable-mixin": "npm:24.9.6"
"@vibrant/color": "npm:4.0.0"
"@vitest/coverage-v8": "npm:4.0.14"
"@vitest/coverage-v8": "npm:4.0.15"
"@vue/web-component-wrapper": "npm:1.3.0"
"@webcomponents/scoped-custom-element-registry": "npm:0.0.10"
"@webcomponents/webcomponentsjs": "npm:2.8.0"
@@ -9343,7 +9343,7 @@ __metadata:
node-vibrant: "npm:4.0.3"
object-hash: "npm:3.0.0"
pinst: "npm:3.0.0"
prettier: "npm:3.7.3"
prettier: "npm:3.7.4"
punycode: "npm:2.3.1"
qr-scanner: "npm:1.4.2"
qrcode: "npm:1.5.4"
@@ -9356,14 +9356,14 @@ __metadata:
stacktrace-js: "npm:2.0.2"
superstruct: "npm:2.0.2"
tar: "npm:7.5.2"
terser-webpack-plugin: "npm:5.3.14"
terser-webpack-plugin: "npm:5.3.15"
tinykeys: "npm:3.0.0"
ts-lit-plugin: "npm:2.0.2"
typescript: "npm:5.9.3"
typescript-eslint: "npm:8.48.0"
typescript-eslint: "npm:8.48.1"
ua-parser-js: "npm:2.0.6"
vite-tsconfig-paths: "npm:5.1.4"
vitest: "npm:4.0.14"
vitest: "npm:4.0.15"
vue: "npm:2.7.16"
vue2-daterange-picker: "npm:0.6.8"
webpack-stats-plugin: "npm:1.1.3"
@@ -12131,12 +12131,12 @@ __metadata:
languageName: node
linkType: hard
"prettier@npm:3.7.3":
version: 3.7.3
resolution: "prettier@npm:3.7.3"
"prettier@npm:3.7.4":
version: 3.7.4
resolution: "prettier@npm:3.7.4"
bin:
prettier: bin/prettier.cjs
checksum: 10/f83ca7e3c69717c1e2f7a95af2d9700eea520c235dfc0f94727a550a4bf0f14eda956a9b2682991b4f1da4605247b3cd3c5547bf2ada4a3ca718bc9ca230040a
checksum: 10/b4d00ea13baed813cb777c444506632fb10faaef52dea526cacd03085f01f6db11fc969ccebedf05bf7d93c3960900994c6adf1b150e28a31afd5cfe7089b313
languageName: node
linkType: hard
@@ -13895,9 +13895,9 @@ __metadata:
languageName: node
linkType: hard
"terser-webpack-plugin@npm:5.3.14":
version: 5.3.14
resolution: "terser-webpack-plugin@npm:5.3.14"
"terser-webpack-plugin@npm:5.3.15":
version: 5.3.15
resolution: "terser-webpack-plugin@npm:5.3.15"
dependencies:
"@jridgewell/trace-mapping": "npm:^0.3.25"
jest-worker: "npm:^27.4.5"
@@ -13913,7 +13913,7 @@ __metadata:
optional: true
uglify-js:
optional: true
checksum: 10/5b7290f7edb179b83cefb8827c12371ddddc088cf251cf58a1c738d82628331ae6604273b61fe991d77411d4bb6b7178c3826aa47edf01b4ee21f973d6c8b8fb
checksum: 10/54059f0fe56c16a1e30032b33b1321051d562b5292adcf88d160c7d8e74779867014e98548ae59ba5283fe60f8725bea37c16b42f2c89ee430382cff74198b7a
languageName: node
linkType: hard
@@ -14026,10 +14026,10 @@ __metadata:
languageName: node
linkType: hard
"tinyexec@npm:^0.3.2":
version: 0.3.2
resolution: "tinyexec@npm:0.3.2"
checksum: 10/b9d5fed3166fb1acd1e7f9a89afcd97ccbe18b9c1af0278e429455f6976d69271ba2d21797e7c36d57d6b05025e525d2882d88c2ab435b60d1ddf2fea361de57
"tinyexec@npm:^1.0.2":
version: 1.0.2
resolution: "tinyexec@npm:1.0.2"
checksum: 10/cb709ed4240e873d3816e67f851d445f5676e0ae3a52931a60ff571d93d388da09108c8057b62351766133ee05ff3159dd56c3a0fbd39a5933c6639ce8771405
languageName: node
linkType: hard
@@ -14321,18 +14321,18 @@ __metadata:
languageName: node
linkType: hard
"typescript-eslint@npm:8.48.0":
version: 8.48.0
resolution: "typescript-eslint@npm:8.48.0"
"typescript-eslint@npm:8.48.1":
version: 8.48.1
resolution: "typescript-eslint@npm:8.48.1"
dependencies:
"@typescript-eslint/eslint-plugin": "npm:8.48.0"
"@typescript-eslint/parser": "npm:8.48.0"
"@typescript-eslint/typescript-estree": "npm:8.48.0"
"@typescript-eslint/utils": "npm:8.48.0"
"@typescript-eslint/eslint-plugin": "npm:8.48.1"
"@typescript-eslint/parser": "npm:8.48.1"
"@typescript-eslint/typescript-estree": "npm:8.48.1"
"@typescript-eslint/utils": "npm:8.48.1"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/9be54df60faf3b5a6d255032b4478170b6f64e38b8396475a2049479d1e3c1f5a23a18bb4d2d6ff685ef92ff8f2af28215772fe33b48148a8cf83a724d0778d1
checksum: 10/2b5318d74f9b8c4cd5d253b4d5249a184a0c5ed9eaf998a604c0d7b816acdc04f40570964d35fc5c93d40171ed3d95b8eef721578b7bedc8433607f4f7e59520
languageName: node
linkType: hard
@@ -14777,17 +14777,17 @@ __metadata:
languageName: node
linkType: hard
"vitest@npm:4.0.14":
version: 4.0.14
resolution: "vitest@npm:4.0.14"
"vitest@npm:4.0.15":
version: 4.0.15
resolution: "vitest@npm:4.0.15"
dependencies:
"@vitest/expect": "npm:4.0.14"
"@vitest/mocker": "npm:4.0.14"
"@vitest/pretty-format": "npm:4.0.14"
"@vitest/runner": "npm:4.0.14"
"@vitest/snapshot": "npm:4.0.14"
"@vitest/spy": "npm:4.0.14"
"@vitest/utils": "npm:4.0.14"
"@vitest/expect": "npm:4.0.15"
"@vitest/mocker": "npm:4.0.15"
"@vitest/pretty-format": "npm:4.0.15"
"@vitest/runner": "npm:4.0.15"
"@vitest/snapshot": "npm:4.0.15"
"@vitest/spy": "npm:4.0.15"
"@vitest/utils": "npm:4.0.15"
es-module-lexer: "npm:^1.7.0"
expect-type: "npm:^1.2.2"
magic-string: "npm:^0.30.21"
@@ -14796,7 +14796,7 @@ __metadata:
picomatch: "npm:^4.0.3"
std-env: "npm:^3.10.0"
tinybench: "npm:^2.9.0"
tinyexec: "npm:^0.3.2"
tinyexec: "npm:^1.0.2"
tinyglobby: "npm:^0.2.15"
tinyrainbow: "npm:^3.0.3"
vite: "npm:^6.0.0 || ^7.0.0"
@@ -14805,10 +14805,10 @@ __metadata:
"@edge-runtime/vm": "*"
"@opentelemetry/api": ^1.9.0
"@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0
"@vitest/browser-playwright": 4.0.14
"@vitest/browser-preview": 4.0.14
"@vitest/browser-webdriverio": 4.0.14
"@vitest/ui": 4.0.14
"@vitest/browser-playwright": 4.0.15
"@vitest/browser-preview": 4.0.15
"@vitest/browser-webdriverio": 4.0.15
"@vitest/ui": 4.0.15
happy-dom: "*"
jsdom: "*"
peerDependenciesMeta:
@@ -14832,7 +14832,7 @@ __metadata:
optional: true
bin:
vitest: vitest.mjs
checksum: 10/f3c6dee29f9882027c5c412e6ca503419c11bcebaecd84e9036ddeadb75a9fddc0f8cadcb785693f7e30492c8068d960a905d2c24094d57cc84070a86b0b4a11
checksum: 10/b6df3d07b3f858ce1efc072d90753bc76d278a29245317b70e7bab0ded8bfaf81fc1e41aa30687365fef6d0e4110727867716440bc745e8948ae217a67f9d77b
languageName: node
linkType: hard