mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-13 11:37:21 +00:00
Compare commits
1 Commits
show-proto
...
checkbox-i
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e93f105542 |
8
.github/workflows/cast_deployment.yaml
vendored
8
.github/workflows/cast_deployment.yaml
vendored
@@ -21,12 +21,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
with:
|
with:
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -56,12 +56,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
|||||||
16
.github/workflows/ci.yaml
vendored
16
.github/workflows/ci.yaml
vendored
@@ -24,9 +24,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -58,9 +58,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -76,9 +76,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -100,9 +100,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
|||||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
with:
|
with:
|
||||||
# We must fetch at least the immediate parents so that if this is
|
# We must fetch at least the immediate parents so that if this is
|
||||||
# a pull request then we can checkout the head.
|
# a pull request then we can checkout the head.
|
||||||
@@ -36,14 +36,14 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
|
uses: github/codeql-action/init@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# 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)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
|
uses: github/codeql-action/autobuild@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -57,4 +57,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
|
uses: github/codeql-action/analyze@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
||||||
|
|||||||
8
.github/workflows/demo_deployment.yaml
vendored
8
.github/workflows/demo_deployment.yaml
vendored
@@ -22,12 +22,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
with:
|
with:
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -57,12 +57,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
|||||||
4
.github/workflows/design_deployment.yaml
vendored
4
.github/workflows/design_deployment.yaml
vendored
@@ -16,10 +16,10 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
|||||||
4
.github/workflows/design_preview.yaml
vendored
4
.github/workflows/design_preview.yaml
vendored
@@ -21,10 +21,10 @@ jobs:
|
|||||||
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
|||||||
4
.github/workflows/nightly.yaml
vendored
4
.github/workflows/nightly.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6
|
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6
|
||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
|||||||
14
.github/workflows/release.yaml
vendored
14
.github/workflows/release.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
contents: write # Required to upload release assets
|
contents: write # Required to upload release assets
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
uses: home-assistant/actions/helpers/verify-version@master
|
uses: home-assistant/actions/helpers/verify-version@master
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -75,7 +75,7 @@ jobs:
|
|||||||
|
|
||||||
# home-assistant/wheels doesn't support SHA pinning
|
# home-assistant/wheels doesn't support SHA pinning
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
uses: home-assistant/wheels@2025.12.0
|
uses: home-assistant/wheels@2025.11.0
|
||||||
with:
|
with:
|
||||||
abi: cp313
|
abi: cp313
|
||||||
tag: musllinux_1_2
|
tag: musllinux_1_2
|
||||||
@@ -91,9 +91,9 @@ jobs:
|
|||||||
contents: write # Required to upload release assets
|
contents: write # Required to upload release assets
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -120,9 +120,9 @@ jobs:
|
|||||||
contents: write # Required to upload release assets
|
contents: write # Required to upload release assets
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
|||||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: 90 days stale policy
|
- name: 90 days stale policy
|
||||||
uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
|
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
days-before-stale: 90
|
days-before-stale: 90
|
||||||
|
|||||||
2
.github/workflows/translations.yaml
vendored
2
.github/workflows/translations.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Upload Translations
|
- name: Upload Translations
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -156,9 +156,7 @@ const createTestTranslation = () =>
|
|||||||
*/
|
*/
|
||||||
const createMasterTranslation = () =>
|
const createMasterTranslation = () =>
|
||||||
gulp
|
gulp
|
||||||
.src([EN_SRC, ...(mergeBackend ? [`${inBackendDir}/en.json`] : [])], {
|
.src([EN_SRC, ...(mergeBackend ? [`${inBackendDir}/en.json`] : [])])
|
||||||
allowEmpty: true,
|
|
||||||
})
|
|
||||||
.pipe(new CustomJSON(lokaliseTransform))
|
.pipe(new CustomJSON(lokaliseTransform))
|
||||||
.pipe(new MergeJSON("en"))
|
.pipe(new MergeJSON("en"))
|
||||||
.pipe(gulp.dest(workDir));
|
.pipe(gulp.dest(workDir));
|
||||||
|
|||||||
@@ -44,24 +44,18 @@ export const mockEnergy = (hass: MockHomeAssistant) => {
|
|||||||
number_energy_price: null,
|
number_energy_price: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
power: [
|
|
||||||
{ stat_rate: "sensor.power_grid" },
|
|
||||||
{ stat_rate: "sensor.power_grid_return" },
|
|
||||||
],
|
|
||||||
cost_adjustment_day: 0,
|
cost_adjustment_day: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "solar",
|
type: "solar",
|
||||||
stat_energy_from: "sensor.solar_production",
|
stat_energy_from: "sensor.solar_production",
|
||||||
stat_rate: "sensor.power_solar",
|
|
||||||
config_entry_solar_forecast: ["solar_forecast"],
|
config_entry_solar_forecast: ["solar_forecast"],
|
||||||
},
|
},
|
||||||
{
|
/* {
|
||||||
type: "battery",
|
type: "battery",
|
||||||
stat_energy_from: "sensor.battery_output",
|
stat_energy_from: "sensor.battery_output",
|
||||||
stat_energy_to: "sensor.battery_input",
|
stat_energy_to: "sensor.battery_input",
|
||||||
stat_rate: "sensor.power_battery",
|
}, */
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: "gas",
|
type: "gas",
|
||||||
stat_energy_from: "sensor.energy_gas",
|
stat_energy_from: "sensor.energy_gas",
|
||||||
@@ -69,48 +63,28 @@ export const mockEnergy = (hass: MockHomeAssistant) => {
|
|||||||
entity_energy_price: null,
|
entity_energy_price: null,
|
||||||
number_energy_price: null,
|
number_energy_price: null,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: "water",
|
|
||||||
stat_energy_from: "sensor.energy_water",
|
|
||||||
stat_cost: "sensor.energy_water_cost",
|
|
||||||
entity_energy_price: null,
|
|
||||||
number_energy_price: null,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
device_consumption: [
|
device_consumption: [
|
||||||
{
|
{
|
||||||
stat_consumption: "sensor.energy_car",
|
stat_consumption: "sensor.energy_car",
|
||||||
stat_rate: "sensor.power_car",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stat_consumption: "sensor.energy_ac",
|
stat_consumption: "sensor.energy_ac",
|
||||||
stat_rate: "sensor.power_ac",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stat_consumption: "sensor.energy_washing_machine",
|
stat_consumption: "sensor.energy_washing_machine",
|
||||||
stat_rate: "sensor.power_washing_machine",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stat_consumption: "sensor.energy_dryer",
|
stat_consumption: "sensor.energy_dryer",
|
||||||
stat_rate: "sensor.power_dryer",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stat_consumption: "sensor.energy_heat_pump",
|
stat_consumption: "sensor.energy_heat_pump",
|
||||||
stat_rate: "sensor.power_heat_pump",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stat_consumption: "sensor.energy_boiler",
|
stat_consumption: "sensor.energy_boiler",
|
||||||
stat_rate: "sensor.power_boiler",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
device_consumption_water: [
|
|
||||||
{
|
|
||||||
stat_consumption: "sensor.water_kitchen",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
stat_consumption: "sensor.water_garden",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
device_consumption_water: [],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
hass.mockWS(
|
hass.mockWS(
|
||||||
|
|||||||
@@ -154,38 +154,6 @@ export const energyEntities = () =>
|
|||||||
unit_of_measurement: "EUR",
|
unit_of_measurement: "EUR",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"sensor.power_grid": {
|
|
||||||
entity_id: "sensor.power_grid",
|
|
||||||
state: "500",
|
|
||||||
attributes: {
|
|
||||||
state_class: "measurement",
|
|
||||||
unit_of_measurement: "W",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"sensor.power_grid_return": {
|
|
||||||
entity_id: "sensor.power_grid_return",
|
|
||||||
state: "-100",
|
|
||||||
attributes: {
|
|
||||||
state_class: "measurement",
|
|
||||||
unit_of_measurement: "W",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"sensor.power_solar": {
|
|
||||||
entity_id: "sensor.power_solar",
|
|
||||||
state: "200",
|
|
||||||
attributes: {
|
|
||||||
state_class: "measurement",
|
|
||||||
unit_of_measurement: "W",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"sensor.power_battery": {
|
|
||||||
entity_id: "sensor.power_battery",
|
|
||||||
state: "100",
|
|
||||||
attributes: {
|
|
||||||
state_class: "measurement",
|
|
||||||
unit_of_measurement: "W",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"sensor.energy_gas_cost": {
|
"sensor.energy_gas_cost": {
|
||||||
entity_id: "sensor.energy_gas_cost",
|
entity_id: "sensor.energy_gas_cost",
|
||||||
state: "2",
|
state: "2",
|
||||||
@@ -203,15 +171,6 @@ export const energyEntities = () =>
|
|||||||
unit_of_measurement: "m³",
|
unit_of_measurement: "m³",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"sensor.energy_water": {
|
|
||||||
entity_id: "sensor.energy_water",
|
|
||||||
state: "4000",
|
|
||||||
attributes: {
|
|
||||||
last_reset: "1970-01-01T00:00:00:00+00",
|
|
||||||
friendly_name: "Water",
|
|
||||||
unit_of_measurement: "L",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"sensor.energy_car": {
|
"sensor.energy_car": {
|
||||||
entity_id: "sensor.energy_car",
|
entity_id: "sensor.energy_car",
|
||||||
state: "4",
|
state: "4",
|
||||||
@@ -266,58 +225,4 @@ export const energyEntities = () =>
|
|||||||
unit_of_measurement: "kWh",
|
unit_of_measurement: "kWh",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"sensor.power_car": {
|
|
||||||
entity_id: "sensor.power_car",
|
|
||||||
state: "40",
|
|
||||||
attributes: {
|
|
||||||
state_class: "measurement",
|
|
||||||
friendly_name: "Electric car",
|
|
||||||
unit_of_measurement: "W",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"sensor.power_ac": {
|
|
||||||
entity_id: "sensor.power_ac",
|
|
||||||
state: "30",
|
|
||||||
attributes: {
|
|
||||||
state_class: "measurement",
|
|
||||||
friendly_name: "Air conditioning",
|
|
||||||
unit_of_measurement: "W",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"sensor.power_washing_machine": {
|
|
||||||
entity_id: "sensor.power_washing_machine",
|
|
||||||
state: "60",
|
|
||||||
attributes: {
|
|
||||||
state_class: "measurement",
|
|
||||||
friendly_name: "Washing machine",
|
|
||||||
unit_of_measurement: "W",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"sensor.power_dryer": {
|
|
||||||
entity_id: "sensor.power_dryer",
|
|
||||||
state: "55",
|
|
||||||
attributes: {
|
|
||||||
state_class: "measurement",
|
|
||||||
friendly_name: "Dryer",
|
|
||||||
unit_of_measurement: "W",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"sensor.power_heat_pump": {
|
|
||||||
entity_id: "sensor.power_heat_pump",
|
|
||||||
state: "60",
|
|
||||||
attributes: {
|
|
||||||
state_class: "measurement",
|
|
||||||
friendly_name: "Heat pump",
|
|
||||||
unit_of_measurement: "W",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"sensor.power_boiler": {
|
|
||||||
entity_id: "sensor.power_boiler",
|
|
||||||
state: "70",
|
|
||||||
attributes: {
|
|
||||||
state_class: "measurement",
|
|
||||||
friendly_name: "Boiler",
|
|
||||||
unit_of_measurement: "W",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,15 +17,17 @@ const generateMeanStatistics = (
|
|||||||
end: Date,
|
end: Date,
|
||||||
// eslint-disable-next-line default-param-last
|
// eslint-disable-next-line default-param-last
|
||||||
period: "5minute" | "hour" | "day" | "month" = "hour",
|
period: "5minute" | "hour" | "day" | "month" = "hour",
|
||||||
|
initValue: number,
|
||||||
maxDiff: number
|
maxDiff: number
|
||||||
): StatisticValue[] => {
|
): StatisticValue[] => {
|
||||||
const statistics: StatisticValue[] = [];
|
const statistics: StatisticValue[] = [];
|
||||||
let currentDate = new Date(start);
|
let currentDate = new Date(start);
|
||||||
currentDate.setMinutes(0, 0, 0);
|
currentDate.setMinutes(0, 0, 0);
|
||||||
|
let lastVal = initValue;
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
while (end > currentDate && currentDate < now) {
|
while (end > currentDate && currentDate < now) {
|
||||||
const delta = Math.random() * maxDiff;
|
const delta = Math.random() * maxDiff;
|
||||||
const mean = delta;
|
const mean = lastVal + delta;
|
||||||
statistics.push({
|
statistics.push({
|
||||||
start: currentDate.getTime(),
|
start: currentDate.getTime(),
|
||||||
end: currentDate.getTime(),
|
end: currentDate.getTime(),
|
||||||
@@ -36,6 +38,7 @@ const generateMeanStatistics = (
|
|||||||
state: mean,
|
state: mean,
|
||||||
sum: null,
|
sum: null,
|
||||||
});
|
});
|
||||||
|
lastVal = mean;
|
||||||
currentDate =
|
currentDate =
|
||||||
period === "day"
|
period === "day"
|
||||||
? addDays(currentDate, 1)
|
? addDays(currentDate, 1)
|
||||||
@@ -333,6 +336,7 @@ export const mockRecorder = (mockHass: MockHomeAssistant) => {
|
|||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
period,
|
period,
|
||||||
|
state,
|
||||||
state * (state > 80 ? 0.05 : 0.1)
|
state * (state > 80 ? 0.05 : 0.1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
title: Adaptive dialog (ha-adaptive-dialog)
|
|
||||||
---
|
|
||||||
@@ -1,732 +0,0 @@
|
|||||||
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><ha-adaptive-dialog></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 (<870px width) or short screens
|
|
||||||
(<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 >
|
|
||||||
870px and height > 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><ha-adaptive-dialog
|
|
||||||
.hass=\${this.hass}
|
|
||||||
open
|
|
||||||
width="medium"
|
|
||||||
header-title="Dialog title"
|
|
||||||
header-subtitle="Dialog subtitle"
|
|
||||||
>
|
|
||||||
<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>Dialog content</div>
|
|
||||||
<ha-dialog-footer slot="footer">
|
|
||||||
<ha-button slot="secondaryAction" variant="plain"
|
|
||||||
>Cancel</ha-button
|
|
||||||
>
|
|
||||||
<ha-button slot="primaryAction" variant="accent">Submit</ha-button>
|
|
||||||
</ha-dialog-footer>
|
|
||||||
</ha-adaptive-dialog></code></pre>
|
|
||||||
|
|
||||||
<p>Example with <code>block-mode-change</code> for forms:</p>
|
|
||||||
|
|
||||||
<pre><code><ha-adaptive-dialog
|
|
||||||
.hass=\${this.hass}
|
|
||||||
open
|
|
||||||
header-title="Edit configuration"
|
|
||||||
block-mode-change
|
|
||||||
>
|
|
||||||
<ha-form .schema=\${schema} .data=\${data}></ha-form>
|
|
||||||
<ha-dialog-footer slot="footer">
|
|
||||||
<ha-button slot="secondaryAction" variant="plain"
|
|
||||||
>Cancel</ha-button
|
|
||||||
>
|
|
||||||
<ha-button slot="primaryAction" variant="accent">Save</ha-button>
|
|
||||||
</ha-dialog-footer>
|
|
||||||
</ha-adaptive-dialog></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><ha-adaptive-dialog .hass=\${this.hass} open>
|
|
||||||
<ha-form autofocus .schema=\${schema}></ha-form>
|
|
||||||
</ha-adaptive-dialog></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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -139,7 +139,7 @@ export class DemoHaWaDialog extends LitElement {
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>large</code></td>
|
<td><code>large</code></td>
|
||||||
<td><code>min(1024px, var(--full-width))</code></td>
|
<td><code>min(720px, var(--full-width))</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>full</code></td>
|
<td><code>full</code></td>
|
||||||
@@ -381,6 +381,10 @@ export class DemoHaWaDialog extends LitElement {
|
|||||||
<td><code>--dialog-z-index</code></td>
|
<td><code>--dialog-z-index</code></td>
|
||||||
<td>Z-index for the dialog.</td>
|
<td>Z-index for the dialog.</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>--dialog-surface-position</code></td>
|
||||||
|
<td>CSS position of the dialog surface.</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>--dialog-surface-margin-top</code></td>
|
<td><code>--dialog-surface-margin-top</code></td>
|
||||||
<td>Top margin for the dialog surface.</td>
|
<td>Top margin for the dialog surface.</td>
|
||||||
|
|||||||
16
package.json
16
package.json
@@ -52,7 +52,7 @@
|
|||||||
"@fullcalendar/list": "6.1.19",
|
"@fullcalendar/list": "6.1.19",
|
||||||
"@fullcalendar/luxon3": "6.1.19",
|
"@fullcalendar/luxon3": "6.1.19",
|
||||||
"@fullcalendar/timegrid": "6.1.19",
|
"@fullcalendar/timegrid": "6.1.19",
|
||||||
"@home-assistant/webawesome": "3.0.0-ha.1",
|
"@home-assistant/webawesome": "3.0.0-ha.0",
|
||||||
"@lezer/highlight": "1.2.3",
|
"@lezer/highlight": "1.2.3",
|
||||||
"@lit-labs/motion": "1.0.9",
|
"@lit-labs/motion": "1.0.9",
|
||||||
"@lit-labs/observers": "2.0.6",
|
"@lit-labs/observers": "2.0.6",
|
||||||
@@ -157,8 +157,8 @@
|
|||||||
"@octokit/auth-oauth-device": "8.0.3",
|
"@octokit/auth-oauth-device": "8.0.3",
|
||||||
"@octokit/plugin-retry": "8.0.3",
|
"@octokit/plugin-retry": "8.0.3",
|
||||||
"@octokit/rest": "22.0.1",
|
"@octokit/rest": "22.0.1",
|
||||||
"@rsdoctor/rspack-plugin": "1.3.12",
|
"@rsdoctor/rspack-plugin": "1.3.11",
|
||||||
"@rspack/core": "1.6.6",
|
"@rspack/core": "1.6.5",
|
||||||
"@rspack/dev-server": "1.1.4",
|
"@rspack/dev-server": "1.1.4",
|
||||||
"@types/babel__plugin-transform-runtime": "7.9.5",
|
"@types/babel__plugin-transform-runtime": "7.9.5",
|
||||||
"@types/chromecast-caf-receiver": "6.0.22",
|
"@types/chromecast-caf-receiver": "6.0.22",
|
||||||
@@ -178,7 +178,7 @@
|
|||||||
"@types/tar": "6.1.13",
|
"@types/tar": "6.1.13",
|
||||||
"@types/ua-parser-js": "0.7.39",
|
"@types/ua-parser-js": "0.7.39",
|
||||||
"@types/webspeechapi": "0.0.29",
|
"@types/webspeechapi": "0.0.29",
|
||||||
"@vitest/coverage-v8": "4.0.15",
|
"@vitest/coverage-v8": "4.0.14",
|
||||||
"babel-loader": "10.0.0",
|
"babel-loader": "10.0.0",
|
||||||
"babel-plugin-template-html-minifier": "4.1.0",
|
"babel-plugin-template-html-minifier": "4.1.0",
|
||||||
"browserslist-useragent-regexp": "4.1.3",
|
"browserslist-useragent-regexp": "4.1.3",
|
||||||
@@ -209,17 +209,17 @@
|
|||||||
"lodash.template": "4.5.0",
|
"lodash.template": "4.5.0",
|
||||||
"map-stream": "0.0.7",
|
"map-stream": "0.0.7",
|
||||||
"pinst": "3.0.0",
|
"pinst": "3.0.0",
|
||||||
"prettier": "3.7.4",
|
"prettier": "3.7.2",
|
||||||
"rspack-manifest-plugin": "5.2.0",
|
"rspack-manifest-plugin": "5.2.0",
|
||||||
"serve": "14.2.5",
|
"serve": "14.2.5",
|
||||||
"sinon": "21.0.0",
|
"sinon": "21.0.0",
|
||||||
"tar": "7.5.2",
|
"tar": "7.5.2",
|
||||||
"terser-webpack-plugin": "5.3.15",
|
"terser-webpack-plugin": "5.3.14",
|
||||||
"ts-lit-plugin": "2.0.2",
|
"ts-lit-plugin": "2.0.2",
|
||||||
"typescript": "5.9.3",
|
"typescript": "5.9.3",
|
||||||
"typescript-eslint": "8.48.1",
|
"typescript-eslint": "8.48.0",
|
||||||
"vite-tsconfig-paths": "5.1.4",
|
"vite-tsconfig-paths": "5.1.4",
|
||||||
"vitest": "4.0.15",
|
"vitest": "4.0.14",
|
||||||
"webpack-stats-plugin": "1.1.3",
|
"webpack-stats-plugin": "1.1.3",
|
||||||
"webpackbar": "7.0.0",
|
"webpackbar": "7.0.0",
|
||||||
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
|
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20251203.0"
|
version = "20251029.0"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
license-files = ["LICENSE*"]
|
license-files = ["LICENSE*"]
|
||||||
description = "The Home Assistant frontend"
|
description = "The Home Assistant frontend"
|
||||||
|
|||||||
@@ -373,7 +373,6 @@ export class StateHistoryChartTimeline extends LitElement {
|
|||||||
itemName: 3,
|
itemName: 3,
|
||||||
},
|
},
|
||||||
renderItem: this._renderItem,
|
renderItem: this._renderItem,
|
||||||
progressive: 0,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,15 @@ import type { TemplateResult } from "lit";
|
|||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { repeat } from "lit/directives/repeat";
|
import { repeat } from "lit/directives/repeat";
|
||||||
|
import type { LabelRegistryEntry } from "../../data/label_registry";
|
||||||
import { computeCssColor } from "../../common/color/compute-color";
|
import { computeCssColor } from "../../common/color/compute-color";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
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 "../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")
|
@customElement("ha-data-table-labels")
|
||||||
class HaDataTableLabels extends LitElement {
|
class HaDataTableLabels extends LitElement {
|
||||||
@@ -28,11 +26,12 @@ class HaDataTableLabels extends LitElement {
|
|||||||
(label) => this._renderLabel(label, true)
|
(label) => this._renderLabel(label, true)
|
||||||
)}
|
)}
|
||||||
${labels.length > 2
|
${labels.length > 2
|
||||||
? html`<ha-dropdown
|
? html`<ha-button-menu
|
||||||
|
absolute
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@click=${stopPropagation}
|
@click=${this._handleIconOverflowMenuOpened}
|
||||||
@wa-select=${this._handleDropdownSelect}
|
@closed=${this._handleIconOverflowMenuClosed}
|
||||||
>
|
>
|
||||||
<ha-label slot="trigger" class="plus" dense>
|
<ha-label slot="trigger" class="plus" dense>
|
||||||
+${labels.length - 2}
|
+${labels.length - 2}
|
||||||
@@ -41,12 +40,12 @@ class HaDataTableLabels extends LitElement {
|
|||||||
labels.slice(2),
|
labels.slice(2),
|
||||||
(label) => label.label_id,
|
(label) => label.label_id,
|
||||||
(label) => html`
|
(label) => html`
|
||||||
<ha-dropdown-item .value=${label.label_id} .item=${label}>
|
<ha-list-item @click=${this._labelClicked} .item=${label}>
|
||||||
${this._renderLabel(label, false)}
|
${this._renderLabel(label, false)}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</ha-dropdown>`
|
</ha-button-menu>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</ha-chip-set>
|
</ha-chip-set>
|
||||||
`;
|
`;
|
||||||
@@ -82,12 +81,21 @@ class HaDataTableLabels extends LitElement {
|
|||||||
fireEvent(this, "label-clicked", { label });
|
fireEvent(this, "label-clicked", { label });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDropdownSelect(
|
protected _handleIconOverflowMenuOpened(e) {
|
||||||
ev: CustomEvent<{ item: HaDropdownItem & { item?: LabelRegistryEntry } }>
|
e.stopPropagation();
|
||||||
) {
|
// If this component is used inside a data table, the z-index of the row
|
||||||
const label = ev.detail?.item?.item;
|
// needs to be increased. Otherwise the ha-button-menu would be displayed
|
||||||
if (label) {
|
// underneath the next row in the table.
|
||||||
fireEvent(this, "label-clicked", { label });
|
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-color: var(--color, var(--grey-color));
|
||||||
--ha-label-background-opacity: 0.5;
|
--ha-label-background-opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
ha-button-menu {
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
.plus {
|
.plus {
|
||||||
--ha-label-background-color: transparent;
|
--ha-label-background-color: transparent;
|
||||||
border: 1px solid var(--divider-color);
|
border: 1px solid var(--divider-color);
|
||||||
|
|||||||
@@ -1,188 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,7 @@ import { LitElement, html } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { getAreaContext } from "../common/entity/context/get_area_context";
|
import { getAreaContext } from "../common/entity/context/get_area_context";
|
||||||
|
import { areaCompare } from "../data/area_registry";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import "./ha-expansion-panel";
|
import "./ha-expansion-panel";
|
||||||
import "./ha-items-display-editor";
|
import "./ha-items-display-editor";
|
||||||
@@ -36,7 +37,11 @@ export class HaAreasDisplayEditor extends LitElement {
|
|||||||
public showNavigationButton = false;
|
public showNavigationButton = false;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const areas = Object.values(this.hass.areas);
|
const compare = areaCompare(this.hass.areas);
|
||||||
|
|
||||||
|
const areas = Object.values(this.hass.areas).sort((areaA, areaB) =>
|
||||||
|
compare(areaA.area_id, areaB.area_id)
|
||||||
|
);
|
||||||
|
|
||||||
const items: DisplayItem[] = areas.map((area) => {
|
const items: DisplayItem[] = areas.map((area) => {
|
||||||
const { floor } = getAreaContext(area, this.hass.floors);
|
const { floor } = getAreaContext(area, this.hass.floors);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import memoizeOne from "memoize-one";
|
|||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { computeFloorName } from "../common/entity/compute_floor_name";
|
import { computeFloorName } from "../common/entity/compute_floor_name";
|
||||||
import { getAreaContext } from "../common/entity/context/get_area_context";
|
import { getAreaContext } from "../common/entity/context/get_area_context";
|
||||||
|
import { areaCompare } from "../data/area_registry";
|
||||||
import type { FloorRegistryEntry } from "../data/floor_registry";
|
import type { FloorRegistryEntry } from "../data/floor_registry";
|
||||||
import { getFloors } from "../panels/lovelace/strategies/areas/helpers/areas-strategy-helper";
|
import { getFloors } from "../panels/lovelace/strategies/areas/helpers/areas-strategy-helper";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
@@ -130,8 +131,11 @@ export class HaAreasFloorsDisplayEditor extends LitElement {
|
|||||||
// update items if floors change
|
// update items if floors change
|
||||||
_hassFloors: HomeAssistant["floors"]
|
_hassFloors: HomeAssistant["floors"]
|
||||||
): Record<string, DisplayItem[]> => {
|
): Record<string, DisplayItem[]> => {
|
||||||
const areas = Object.values(hassAreas);
|
const compare = areaCompare(hassAreas);
|
||||||
|
|
||||||
|
const areas = Object.values(hassAreas).sort((areaA, areaB) =>
|
||||||
|
compare(areaA.area_id, areaB.area_id)
|
||||||
|
);
|
||||||
const groupedItems: Record<string, DisplayItem[]> = areas.reduce(
|
const groupedItems: Record<string, DisplayItem[]> = areas.reduce(
|
||||||
(acc, area) => {
|
(acc, area) => {
|
||||||
const { floor } = getAreaContext(area, this.hass.floors);
|
const { floor } = getAreaContext(area, this.hass.floors);
|
||||||
|
|||||||
@@ -2,13 +2,12 @@ import "@home-assistant/webawesome/dist/components/drawer/drawer";
|
|||||||
import { css, html, LitElement, type PropertyValues } from "lit";
|
import { css, html, LitElement, type PropertyValues } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { SwipeGestureRecognizer } from "../common/util/swipe-gesture-recognizer";
|
import { SwipeGestureRecognizer } from "../common/util/swipe-gesture-recognizer";
|
||||||
import { ScrollableFadeMixin } from "../mixins/scrollable-fade-mixin";
|
|
||||||
import { haStyleScrollbar } from "../resources/styles";
|
import { haStyleScrollbar } from "../resources/styles";
|
||||||
|
|
||||||
export const BOTTOM_SHEET_ANIMATION_DURATION_MS = 300;
|
export const BOTTOM_SHEET_ANIMATION_DURATION_MS = 300;
|
||||||
|
|
||||||
@customElement("ha-bottom-sheet")
|
@customElement("ha-bottom-sheet")
|
||||||
export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
export class HaBottomSheet extends LitElement {
|
||||||
@property({ type: Boolean }) public open = false;
|
@property({ type: Boolean }) public open = false;
|
||||||
|
|
||||||
@property({ type: Boolean, reflect: true, attribute: "flexcontent" })
|
@property({ type: Boolean, reflect: true, attribute: "flexcontent" })
|
||||||
@@ -18,12 +17,6 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
|||||||
|
|
||||||
@query("#drawer") private _drawer!: HTMLElement;
|
@query("#drawer") private _drawer!: HTMLElement;
|
||||||
|
|
||||||
@query("#body") private _bodyElement!: HTMLDivElement;
|
|
||||||
|
|
||||||
protected get scrollableElement(): HTMLElement | null {
|
|
||||||
return this._bodyElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _gestureRecognizer = new SwipeGestureRecognizer();
|
private _gestureRecognizer = new SwipeGestureRecognizer();
|
||||||
|
|
||||||
private _isDragging = false;
|
private _isDragging = false;
|
||||||
@@ -56,13 +49,9 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
|||||||
@touchstart=${this._handleTouchStart}
|
@touchstart=${this._handleTouchStart}
|
||||||
>
|
>
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
<div class="content-wrapper">
|
|
||||||
<div id="body" class="body ha-scrollbar">
|
<div id="body" class="body ha-scrollbar">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
${this.renderScrollableFades()}
|
|
||||||
</div>
|
|
||||||
<slot name="footer"></slot>
|
|
||||||
</wa-drawer>
|
</wa-drawer>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -178,9 +167,7 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
|||||||
this._isDragging = false;
|
this._isDragging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static styles = [
|
||||||
return [
|
|
||||||
...super.styles,
|
|
||||||
haStyleScrollbar,
|
haStyleScrollbar,
|
||||||
css`
|
css`
|
||||||
wa-drawer {
|
wa-drawer {
|
||||||
@@ -221,13 +208,6 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.content-wrapper {
|
|
||||||
position: relative;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
:host([flexcontent]) .body {
|
:host([flexcontent]) .body {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
@@ -239,26 +219,8 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
|||||||
var(--safe-area-inset-left)
|
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 {
|
declare global {
|
||||||
|
|||||||
@@ -167,33 +167,30 @@ export class HaFilterLabels extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _labelSelected(ev: CustomEvent<SelectedDetail<Set<number>>>) {
|
private async _labelSelected(ev: CustomEvent<SelectedDetail<Set<number>>>) {
|
||||||
|
if (!ev.detail.index.size) {
|
||||||
|
fireEvent(this, "data-table-filter-changed", {
|
||||||
|
value: [],
|
||||||
|
items: undefined,
|
||||||
|
});
|
||||||
|
this.value = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value: string[] = [];
|
||||||
const filteredLabels = this._filteredLabels(
|
const filteredLabels = this._filteredLabels(
|
||||||
this._labels,
|
this._labels,
|
||||||
this._filter,
|
this._filter,
|
||||||
this.value
|
this.value
|
||||||
);
|
);
|
||||||
|
|
||||||
const filteredLabelIds = new Set(filteredLabels.map((l) => l.label_id));
|
|
||||||
|
|
||||||
// Keep previously selected labels that are not in the current filtered view
|
|
||||||
const preservedLabels = (this.value || []).filter(
|
|
||||||
(id) => !filteredLabelIds.has(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Build the new selection from the filtered labels based on selected indices
|
|
||||||
const newlySelectedLabels: string[] = [];
|
|
||||||
for (const index of ev.detail.index) {
|
for (const index of ev.detail.index) {
|
||||||
const labelId = filteredLabels[index]?.label_id;
|
const labelId = filteredLabels[index].label_id;
|
||||||
if (labelId) {
|
value.push(labelId);
|
||||||
newlySelectedLabels.push(labelId);
|
|
||||||
}
|
}
|
||||||
}
|
this.value = value;
|
||||||
|
|
||||||
const value = [...preservedLabels, ...newlySelectedLabels];
|
|
||||||
this.value = value.length ? value : [];
|
|
||||||
|
|
||||||
fireEvent(this, "data-table-filter-changed", {
|
fireEvent(this, "data-table-filter-changed", {
|
||||||
value: value.length ? value : undefined,
|
value,
|
||||||
items: undefined,
|
items: undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,6 @@ export class HaGridSizeEditor extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public step = 1;
|
@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 };
|
@state() public _localValue?: CardGridSize = { rows: 1, columns: 1 };
|
||||||
|
|
||||||
protected willUpdate(changedProperties) {
|
protected willUpdate(changedProperties) {
|
||||||
@@ -48,11 +42,9 @@ export class HaGridSizeEditor extends LitElement {
|
|||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const disabledColumns =
|
const disabledColumns =
|
||||||
this.columnsDisabled ||
|
this.columnMin !== undefined && this.columnMin === this.columnMax;
|
||||||
(this.columnMin !== undefined && this.columnMin === this.columnMax);
|
|
||||||
const disabledRows =
|
const disabledRows =
|
||||||
this.rowsDisabled ||
|
this.rowMin !== undefined && this.rowMin === this.rowMax;
|
||||||
(this.rowMin !== undefined && this.rowMin === this.rowMax);
|
|
||||||
|
|
||||||
const autoHeight = this._localValue?.rows === "auto";
|
const autoHeight = this._localValue?.rows === "auto";
|
||||||
const fullWidth = this._localValue?.columns === "full";
|
const fullWidth = this._localValue?.columns === "full";
|
||||||
@@ -80,7 +72,7 @@ export class HaGridSizeEditor extends LitElement {
|
|||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
@slider-moved=${this._sliderMoved}
|
@slider-moved=${this._sliderMoved}
|
||||||
.disabled=${disabledColumns}
|
.disabled=${disabledColumns}
|
||||||
tooltip-mode=${disabledColumns ? "never" : "always"}
|
tooltip-mode="always"
|
||||||
></ha-grid-layout-slider>
|
></ha-grid-layout-slider>
|
||||||
|
|
||||||
<ha-grid-layout-slider
|
<ha-grid-layout-slider
|
||||||
@@ -96,7 +88,7 @@ export class HaGridSizeEditor extends LitElement {
|
|||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
@slider-moved=${this._sliderMoved}
|
@slider-moved=${this._sliderMoved}
|
||||||
.disabled=${disabledRows}
|
.disabled=${disabledRows}
|
||||||
tooltip-mode=${disabledRows ? "never" : "always"}
|
tooltip-mode="always"
|
||||||
></ha-grid-layout-slider>
|
></ha-grid-layout-slider>
|
||||||
${!this.isDefault
|
${!this.isDefault
|
||||||
? html`
|
? html`
|
||||||
|
|||||||
@@ -99,7 +99,10 @@ class HaMarkdownElement extends ReactiveElement {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
render(h(unsafeHTML(elements.join(""))), this.renderRoot);
|
render(
|
||||||
|
elements.map((e) => h(unsafeHTML(e))),
|
||||||
|
this.renderRoot
|
||||||
|
);
|
||||||
|
|
||||||
this._resize();
|
this._resize();
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ export class HaMarkdown extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public cache = false;
|
@property({ type: Boolean }) public cache = false;
|
||||||
|
|
||||||
@query("ha-markdown-element") private _markdownElement?: ReactiveElement;
|
@query("ha-markdown-element") private _markdownElement!: ReactiveElement;
|
||||||
|
|
||||||
protected async getUpdateComplete() {
|
protected async getUpdateComplete() {
|
||||||
const result = await super.getUpdateComplete();
|
const result = await super.getUpdateComplete();
|
||||||
await this._markdownElement?.updateComplete;
|
await this._markdownElement.updateComplete;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import memoizeOne from "memoize-one";
|
|||||||
import { tinykeys } from "tinykeys";
|
import { tinykeys } from "tinykeys";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
||||||
import { ScrollableFadeMixin } from "../mixins/scrollable-fade-mixin";
|
|
||||||
import { HaFuse } from "../resources/fuse";
|
import { HaFuse } from "../resources/fuse";
|
||||||
import { haStyleScrollbar } from "../resources/styles";
|
import { haStyleScrollbar } from "../resources/styles";
|
||||||
import { loadVirtualizer } from "../resources/virtualizer";
|
import { loadVirtualizer } from "../resources/virtualizer";
|
||||||
@@ -60,7 +59,7 @@ export type PickerComboBoxSearchFn<T extends PickerComboBoxItem> = (
|
|||||||
) => T[];
|
) => T[];
|
||||||
|
|
||||||
@customElement("ha-picker-combo-box")
|
@customElement("ha-picker-combo-box")
|
||||||
export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
export class HaPickerComboBox extends LitElement {
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
// eslint-disable-next-line lit/no-native-attributes
|
// eslint-disable-next-line lit/no-native-attributes
|
||||||
@@ -127,10 +126,6 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
|||||||
|
|
||||||
@state() private _items: (PickerComboBoxItem | string)[] = [];
|
@state() private _items: (PickerComboBoxItem | string)[] = [];
|
||||||
|
|
||||||
protected get scrollableElement(): HTMLElement | null {
|
|
||||||
return this._virtualizerElement as HTMLElement | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@state() private _sectionTitle?: string;
|
@state() private _sectionTitle?: string;
|
||||||
|
|
||||||
private _allItems: (PickerComboBoxItem | string)[] = [];
|
private _allItems: (PickerComboBoxItem | string)[] = [];
|
||||||
@@ -185,7 +180,6 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
<div class="virtualizer-wrapper">
|
|
||||||
<lit-virtualizer
|
<lit-virtualizer
|
||||||
.keyFunction=${this._keyFunction}
|
.keyFunction=${this._keyFunction}
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@@ -198,9 +192,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
|||||||
@focus=${this._focusList}
|
@focus=${this._focusList}
|
||||||
@visibilityChanged=${this._visibilityChanged}
|
@visibilityChanged=${this._visibilityChanged}
|
||||||
>
|
>
|
||||||
</lit-virtualizer>
|
</lit-virtualizer>`;
|
||||||
${this.renderScrollableFades()}
|
|
||||||
</div>`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderSectionButtons() {
|
private _renderSectionButtons() {
|
||||||
@@ -592,9 +584,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
|||||||
private _keyFunction = (item: PickerComboBoxItem | string) =>
|
private _keyFunction = (item: PickerComboBoxItem | string) =>
|
||||||
typeof item === "string" ? item : item.id;
|
typeof item === "string" ? item : item.id;
|
||||||
|
|
||||||
static get styles() {
|
static styles = [
|
||||||
return [
|
|
||||||
...super.styles,
|
|
||||||
haStyleScrollbar,
|
haStyleScrollbar,
|
||||||
css`
|
css`
|
||||||
:host {
|
:host {
|
||||||
@@ -627,14 +617,6 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.virtualizer-wrapper {
|
|
||||||
position: relative;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lit-virtualizer {
|
lit-virtualizer {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
@@ -752,7 +734,6 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import { customElement, property, query, state } from "lit/decorators";
|
|||||||
import { prepareZXingModule } from "barcode-detector";
|
import { prepareZXingModule } from "barcode-detector";
|
||||||
import type QrScanner from "qr-scanner";
|
import type QrScanner from "qr-scanner";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||||
import { addExternalBarCodeListener } from "../external_app/external_app_entrypoint";
|
import { addExternalBarCodeListener } from "../external_app/external_app_entrypoint";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import "./ha-alert";
|
import "./ha-alert";
|
||||||
import "./ha-button";
|
import "./ha-button";
|
||||||
import "./ha-dropdown";
|
import "./ha-button-menu";
|
||||||
import "./ha-dropdown-item";
|
import "./ha-list-item";
|
||||||
import type { HaDropdownItem } from "./ha-dropdown-item";
|
|
||||||
import "./ha-spinner";
|
import "./ha-spinner";
|
||||||
import "./ha-textfield";
|
import "./ha-textfield";
|
||||||
import type { HaTextField } from "./ha-textfield";
|
import type { HaTextField } from "./ha-textfield";
|
||||||
@@ -52,8 +52,6 @@ class HaQrScanner extends LitElement {
|
|||||||
|
|
||||||
@state() private _warning?: string;
|
@state() private _warning?: string;
|
||||||
|
|
||||||
@state() private _selectedCamera?: string;
|
|
||||||
|
|
||||||
private _qrScanner?: QrScanner;
|
private _qrScanner?: QrScanner;
|
||||||
|
|
||||||
private _qrNotFoundCount = 0;
|
private _qrNotFoundCount = 0;
|
||||||
@@ -123,7 +121,7 @@ class HaQrScanner extends LitElement {
|
|||||||
!this._error &&
|
!this._error &&
|
||||||
this._cameras &&
|
this._cameras &&
|
||||||
this._cameras.length > 1
|
this._cameras.length > 1
|
||||||
? html`<ha-dropdown @wa-select=${this._handleDropdownSelect}>
|
? html`<ha-button-menu fixed @closed=${stopPropagation}>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@@ -133,17 +131,15 @@ class HaQrScanner extends LitElement {
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
${this._cameras!.map(
|
${this._cameras!.map(
|
||||||
(camera) => html`
|
(camera) => html`
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
.value=${camera.id}
|
.value=${camera.id}
|
||||||
class=${this._selectedCamera === camera.id
|
@click=${this._cameraChanged}
|
||||||
? "selected"
|
|
||||||
: ""}
|
|
||||||
>
|
>
|
||||||
${camera.label}
|
${camera.label}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</ha-dropdown>`
|
</ha-button-menu>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>`
|
</div>`
|
||||||
: html`<ha-alert alert-type="warning">
|
: html`<ha-alert alert-type="warning">
|
||||||
@@ -209,9 +205,6 @@ class HaQrScanner extends LitElement {
|
|||||||
|
|
||||||
private async _listCameras(qrScanner: typeof QrScanner): Promise<void> {
|
private async _listCameras(qrScanner: typeof QrScanner): Promise<void> {
|
||||||
this._cameras = await qrScanner.listCameras(true);
|
this._cameras = await qrScanner.listCameras(true);
|
||||||
if (this._cameras.length > 0) {
|
|
||||||
this._selectedCamera = this._cameras[0].id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _qrCodeError = (err: any) => {
|
private _qrCodeError = (err: any) => {
|
||||||
@@ -259,12 +252,8 @@ class HaQrScanner extends LitElement {
|
|||||||
this._qrCodeScanned(this._manualInput!.value);
|
this._qrCodeScanned(this._manualInput!.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
private _cameraChanged(ev: CustomEvent): void {
|
||||||
const cameraId = ev.detail?.item?.value;
|
this._qrScanner?.setCamera((ev.target as any).value);
|
||||||
if (cameraId) {
|
|
||||||
this._selectedCamera = cameraId;
|
|
||||||
this._qrScanner?.setCamera(cameraId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _openExternalScanner() {
|
private _openExternalScanner() {
|
||||||
@@ -370,7 +359,7 @@ class HaQrScanner extends LitElement {
|
|||||||
#canvas-container {
|
#canvas-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
ha-icon-button {
|
ha-button-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 8px;
|
bottom: 8px;
|
||||||
right: 8px;
|
right: 8px;
|
||||||
@@ -380,9 +369,6 @@ class HaQrScanner extends LitElement {
|
|||||||
color: white;
|
color: white;
|
||||||
border-radius: var(--ha-border-radius-circle);
|
border-radius: var(--ha-border-radius-circle);
|
||||||
}
|
}
|
||||||
ha-dropdown-item.selected {
|
|
||||||
font-weight: var(--ha-font-weight-bold);
|
|
||||||
}
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import { subscribeLabFeature } from "../data/labs";
|
import { subscribeLabFeatures } from "../data/labs";
|
||||||
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
||||||
|
|
||||||
interface Snowflake {
|
interface Snowflake {
|
||||||
@@ -10,7 +10,7 @@ interface Snowflake {
|
|||||||
size: number;
|
size: number;
|
||||||
duration: number;
|
duration: number;
|
||||||
delay: number;
|
delay: number;
|
||||||
rotation: number;
|
blur: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-snowflakes")
|
@customElement("ha-snowflakes")
|
||||||
@@ -27,14 +27,13 @@ export class HaSnowflakes extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
public hassSubscribe() {
|
public hassSubscribe() {
|
||||||
return [
|
return [
|
||||||
subscribeLabFeature(
|
subscribeLabFeatures(this.hass!.connection, (features) => {
|
||||||
this.hass!.connection,
|
this._enabled =
|
||||||
"frontend",
|
features.find(
|
||||||
"winter_mode",
|
(f) =>
|
||||||
(feature) => {
|
f.domain === "frontend" && f.preview_feature === "winter_mode"
|
||||||
this._enabled = feature.enabled;
|
)?.enabled ?? false;
|
||||||
}
|
}),
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +51,7 @@ export class HaSnowflakes extends SubscribeMixin(LitElement) {
|
|||||||
size: Math.random() * 12 + 8, // Random size between 8-20px
|
size: Math.random() * 12 + 8, // Random size between 8-20px
|
||||||
duration: Math.random() * 8 + 8, // Random duration between 8-16s
|
duration: Math.random() * 8 + 8, // Random duration between 8-16s
|
||||||
delay: Math.random() * 8, // Random delay between 0-8s
|
delay: Math.random() * 8, // Random delay between 0-8s
|
||||||
rotation: Math.random() * 720 - 360, // Random starting rotation -360 to 360deg
|
blur: Math.random() * 1, // Random blur between 0-1px
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this._snowflakes = snowflakes;
|
this._snowflakes = snowflakes;
|
||||||
@@ -76,27 +75,20 @@ export class HaSnowflakes extends SubscribeMixin(LitElement) {
|
|||||||
<div class="snowflakes ${isDark ? "dark" : "light"}" aria-hidden="true">
|
<div class="snowflakes ${isDark ? "dark" : "light"}" aria-hidden="true">
|
||||||
${this._snowflakes.map(
|
${this._snowflakes.map(
|
||||||
(flake) => html`
|
(flake) => html`
|
||||||
<svg
|
<div
|
||||||
class="snowflake ${this.narrow && flake.id >= 30
|
class="snowflake ${this.narrow && flake.id >= 30
|
||||||
? "hide-narrow"
|
? "hide-narrow"
|
||||||
: ""}"
|
: ""}"
|
||||||
style="
|
style="
|
||||||
left: ${flake.left}%;
|
left: ${flake.left}%;
|
||||||
width: ${flake.size}px;
|
font-size: ${flake.size}px;
|
||||||
height: ${flake.size}px;
|
|
||||||
animation-duration: ${flake.duration}s;
|
animation-duration: ${flake.duration}s;
|
||||||
animation-delay: ${flake.delay}s;
|
animation-delay: ${flake.delay}s;
|
||||||
--rotation: ${flake.rotation}deg;
|
filter: blur(${flake.blur}px);
|
||||||
"
|
"
|
||||||
viewBox="0 0 16 16"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
>
|
||||||
<path
|
❄
|
||||||
d="M7.991 0a.644.644 0 0 1 .283 1.221v2.553l.986-.988a.645.645 0 0 1 .612-.839.644.644 0 1 1-.222 1.247l-1.376 1.38V7.52l1.65-.954.466-1.879a.645.645 0 0 1 .1-1.042.643.643 0 1 1 .445 1.189l-.363 1.356 3.145-1.82a.643.643 0 1 1 .282.49l-2.205 1.277 1.347.361a.643.643 0 1 1-.158.543l-1.88-.505L8.573 8l1.632.945 1.858-.535a.64.64 0 0 1 .95-.434.643.643 0 1 1-.805.98l-1.354.364L14 11.14a.641.641 0 0 1 .914.855.643.643 0 0 1-1.197-.366l-2.205-1.276.36 1.35a.642.642 0 0 1 .419.95.643.643 0 1 1-.967-.816l-.503-1.884L8.273 8.48v1.909l1.39 1.344a.644.644 0 1 1 .208 1.252.644.644 0 0 1-.606-.852l-.991-.994v3.64A.644.644 0 0 1 7.99 16a.644.644 0 0 1-.282-1.221v-2.553l-.986.988a.645.645 0 0 1-.612.839.644.644 0 1 1 .222-1.247l1.376-1.38V8.5l-1.632.945-.467 1.879q.079.068.134.163a.643.643 0 1 1-.68-.31l.364-1.357-3.145 1.82A.643.643 0 1 1 2 11.15l2.205-1.276-1.347-.361a.643.643 0 1 1 .158-.543l1.88.505L7.444 8l-1.65-.954-1.857.534a.64.64 0 0 1-.95.434.643.643 0 1 1 .805-.98l1.354-.364L2 4.85a.641.641 0 0 1-.914-.855.643.643 0 0 1 1.197.366l2.205 1.276-.36-1.35a.642.642 0 0 1-.419-.95.643.643 0 1 1 .967.816l.503 1.884L7.71 7.5V5.611L6.32 4.267a.644.644 0 1 1-.208-1.252.644.644 0 0 1 .607.852l.991.994V1.22A.644.644 0 0 1 7.991 0"
|
</div>
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -136,10 +128,16 @@ export class HaSnowflakes extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
.light .snowflake {
|
.light .snowflake {
|
||||||
color: #00bcd4;
|
color: #00bcd4;
|
||||||
|
text-shadow:
|
||||||
|
0 0 5px #00bcd4,
|
||||||
|
0 0 10px #00e5ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .snowflake {
|
.dark .snowflake {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
text-shadow:
|
||||||
|
0 0 5px rgba(255, 255, 255, 0.8),
|
||||||
|
0 0 10px rgba(255, 255, 255, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.snowflake.hide-narrow {
|
.snowflake.hide-narrow {
|
||||||
@@ -148,23 +146,19 @@ export class HaSnowflakes extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@keyframes fall {
|
@keyframes fall {
|
||||||
0% {
|
0% {
|
||||||
transform: translateY(-10vh) translateX(0) rotate(var(--rotation));
|
transform: translateY(-10vh) translateX(0);
|
||||||
}
|
}
|
||||||
25% {
|
25% {
|
||||||
transform: translateY(30vh) translateX(10px)
|
transform: translateY(30vh) translateX(10px);
|
||||||
rotate(calc(var(--rotation) + 25deg));
|
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
transform: translateY(60vh) translateX(-10px)
|
transform: translateY(60vh) translateX(-10px);
|
||||||
rotate(calc(var(--rotation) + 50deg));
|
|
||||||
}
|
}
|
||||||
75% {
|
75% {
|
||||||
transform: translateY(85vh) translateX(10px)
|
transform: translateY(85vh) translateX(10px);
|
||||||
rotate(calc(var(--rotation) + 75deg));
|
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
transform: translateY(120vh) translateX(0)
|
transform: translateY(120vh) translateX(0);
|
||||||
rotate(calc(var(--rotation) + 100deg));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
} from "lit/decorators";
|
} from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { ScrollableFadeMixin } from "../mixins/scrollable-fade-mixin";
|
|
||||||
import { haStyleScrollbar } from "../resources/styles";
|
import { haStyleScrollbar } from "../resources/styles";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import "./ha-dialog-header";
|
import "./ha-dialog-header";
|
||||||
@@ -50,6 +49,7 @@ export type DialogWidth = "small" | "medium" | "large" | "full";
|
|||||||
* @cssprop --ha-dialog-surface-background - Dialog background color.
|
* @cssprop --ha-dialog-surface-background - Dialog background color.
|
||||||
* @cssprop --ha-dialog-border-radius - Border radius of the dialog surface.
|
* @cssprop --ha-dialog-border-radius - Border radius of the dialog surface.
|
||||||
* @cssprop --dialog-z-index - Z-index for the dialog.
|
* @cssprop --dialog-z-index - Z-index for the dialog.
|
||||||
|
* @cssprop --dialog-surface-position - CSS position of the dialog surface.
|
||||||
* @cssprop --dialog-surface-margin-top - Top margin for the dialog surface.
|
* @cssprop --dialog-surface-margin-top - Top margin for the dialog surface.
|
||||||
*
|
*
|
||||||
* @attr {boolean} open - Controls the dialog open state.
|
* @attr {boolean} open - Controls the dialog open state.
|
||||||
@@ -73,7 +73,7 @@ export type DialogWidth = "small" | "medium" | "large" | "full";
|
|||||||
* @see https://github.com/home-assistant/frontend/issues/27143
|
* @see https://github.com/home-assistant/frontend/issues/27143
|
||||||
*/
|
*/
|
||||||
@customElement("ha-wa-dialog")
|
@customElement("ha-wa-dialog")
|
||||||
export class HaWaDialog extends ScrollableFadeMixin(LitElement) {
|
export class HaWaDialog extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: "aria-labelledby" })
|
@property({ attribute: "aria-labelledby" })
|
||||||
@@ -114,10 +114,6 @@ export class HaWaDialog extends ScrollableFadeMixin(LitElement) {
|
|||||||
@state()
|
@state()
|
||||||
private _bodyScrolled = false;
|
private _bodyScrolled = false;
|
||||||
|
|
||||||
protected get scrollableElement(): HTMLElement | null {
|
|
||||||
return this.bodyContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected updated(
|
protected updated(
|
||||||
changedProperties: Map<string | number | symbol, unknown>
|
changedProperties: Map<string | number | symbol, unknown>
|
||||||
): void {
|
): void {
|
||||||
@@ -166,12 +162,9 @@ export class HaWaDialog extends ScrollableFadeMixin(LitElement) {
|
|||||||
<slot name="headerActionItems" slot="actionItems"></slot>
|
<slot name="headerActionItems" slot="actionItems"></slot>
|
||||||
</ha-dialog-header>
|
</ha-dialog-header>
|
||||||
</slot>
|
</slot>
|
||||||
<div class="content-wrapper">
|
|
||||||
<div class="body ha-scrollbar" @scroll=${this._handleBodyScroll}>
|
<div class="body ha-scrollbar" @scroll=${this._handleBodyScroll}>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
${this.renderScrollableFades()}
|
|
||||||
</div>
|
|
||||||
<slot name="footer" slot="footer"></slot>
|
<slot name="footer" slot="footer"></slot>
|
||||||
</wa-dialog>
|
</wa-dialog>
|
||||||
`;
|
`;
|
||||||
@@ -207,16 +200,11 @@ export class HaWaDialog extends ScrollableFadeMixin(LitElement) {
|
|||||||
this._bodyScrolled = (ev.target as HTMLDivElement).scrollTop > 0;
|
this._bodyScrolled = (ev.target as HTMLDivElement).scrollTop > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static styles = [
|
||||||
return [
|
|
||||||
...super.styles,
|
|
||||||
haStyleScrollbar,
|
haStyleScrollbar,
|
||||||
css`
|
css`
|
||||||
wa-dialog {
|
wa-dialog {
|
||||||
--full-width: var(
|
--full-width: var(--ha-dialog-width-full, min(95vw, var(--safe-width)));
|
||||||
--ha-dialog-width-full,
|
|
||||||
min(95vw, var(--safe-width))
|
|
||||||
);
|
|
||||||
--width: min(var(--ha-dialog-width-md, 580px), var(--full-width));
|
--width: min(var(--ha-dialog-width-md, 580px), var(--full-width));
|
||||||
--spacing: var(--dialog-content-padding, var(--ha-space-6));
|
--spacing: var(--dialog-content-padding, var(--ha-space-6));
|
||||||
--show-duration: var(--ha-dialog-show-duration, 200ms);
|
--show-duration: var(--ha-dialog-show-duration, 200ms);
|
||||||
@@ -256,6 +244,7 @@ export class HaWaDialog extends ScrollableFadeMixin(LitElement) {
|
|||||||
calc(var(--safe-height) - var(--ha-space-20))
|
calc(var(--safe-height) - var(--ha-space-20))
|
||||||
);
|
);
|
||||||
min-height: var(--ha-dialog-min-height);
|
min-height: var(--ha-dialog-min-height);
|
||||||
|
position: var(--dialog-surface-position, relative);
|
||||||
margin-top: var(--dialog-surface-margin-top, auto);
|
margin-top: var(--dialog-surface-margin-top, auto);
|
||||||
/* Used to offset the dialog from the safe areas when space is limited */
|
/* Used to offset the dialog from the safe areas when space is limited */
|
||||||
transform: translate(
|
transform: translate(
|
||||||
@@ -341,20 +330,11 @@ export class HaWaDialog extends ScrollableFadeMixin(LitElement) {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-wrapper {
|
|
||||||
position: relative;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
position: var(--dialog-content-position, relative);
|
position: var(--dialog-content-position, relative);
|
||||||
padding: var(
|
padding: 0 var(--dialog-content-padding, var(--ha-space-6))
|
||||||
--dialog-content-padding,
|
var(--dialog-content-padding, var(--ha-space-6))
|
||||||
0 var(--ha-space-6) var(--ha-space-6) var(--ha-space-6)
|
var(--dialog-content-padding, var(--ha-space-6));
|
||||||
);
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
@@ -380,7 +360,6 @@ export class HaWaDialog extends ScrollableFadeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|||||||
@@ -223,7 +223,6 @@ const getAreasAndFloorsItems = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
let outputAreas = areas;
|
let outputAreas = areas;
|
||||||
let outputFloors = floors;
|
|
||||||
|
|
||||||
let areaIds: string[] | undefined;
|
let areaIds: string[] | undefined;
|
||||||
|
|
||||||
@@ -255,29 +254,9 @@ const getAreasAndFloorsItems = (
|
|||||||
outputAreas = outputAreas.filter(
|
outputAreas = outputAreas.filter(
|
||||||
(area) => !area.floor_id || !excludeFloors!.includes(area.floor_id)
|
(area) => !area.floor_id || !excludeFloors!.includes(area.floor_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
outputFloors = outputFloors.filter(
|
|
||||||
(floor) => !excludeFloors!.includes(floor.floor_id)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const hierarchy = getAreasFloorHierarchy(floors, outputAreas);
|
||||||
entityFilter ||
|
|
||||||
deviceFilter ||
|
|
||||||
includeDomains ||
|
|
||||||
excludeDomains ||
|
|
||||||
includeDeviceClasses
|
|
||||||
) {
|
|
||||||
// Ensure we only include floors that have areas with the filtered entities/devices
|
|
||||||
const validFloorIds = new Set(
|
|
||||||
outputAreas.map((area) => area.floor_id).filter((id) => id)
|
|
||||||
);
|
|
||||||
outputFloors = outputFloors.filter((floor) =>
|
|
||||||
validFloorIds.has(floor.floor_id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const hierarchy = getAreasFloorHierarchy(outputFloors, outputAreas);
|
|
||||||
|
|
||||||
const items: (
|
const items: (
|
||||||
| FloorComboBoxItem
|
| FloorComboBoxItem
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { stringCompare } from "../common/string/compare";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import type { DeviceRegistryEntry } from "./device_registry";
|
import type { DeviceRegistryEntry } from "./device_registry";
|
||||||
import type {
|
import type {
|
||||||
@@ -104,3 +105,22 @@ export const getAreaDeviceLookup = (
|
|||||||
}
|
}
|
||||||
return areaDeviceLookup;
|
return areaDeviceLookup;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const areaCompare =
|
||||||
|
(entries?: HomeAssistant["areas"], order?: string[]) =>
|
||||||
|
(a: string, b: string) => {
|
||||||
|
const indexA = order ? order.indexOf(a) : -1;
|
||||||
|
const indexB = order ? order.indexOf(b) : -1;
|
||||||
|
if (indexA === -1 && indexB === -1) {
|
||||||
|
const nameA = entries?.[a]?.name ?? a;
|
||||||
|
const nameB = entries?.[b]?.name ?? b;
|
||||||
|
return stringCompare(nameA, nameB);
|
||||||
|
}
|
||||||
|
if (indexA === -1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (indexB === -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return indexA - indexB;
|
||||||
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
isLastDayOfMonth,
|
isLastDayOfMonth,
|
||||||
addYears,
|
addYears,
|
||||||
} from "date-fns";
|
} from "date-fns";
|
||||||
import type { Collection, HassEntity } from "home-assistant-js-websocket";
|
import type { Collection } from "home-assistant-js-websocket";
|
||||||
import { getCollection } from "home-assistant-js-websocket";
|
import { getCollection } from "home-assistant-js-websocket";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import {
|
import {
|
||||||
@@ -1361,37 +1361,3 @@ export const calculateSolarConsumedGauge = (
|
|||||||
}
|
}
|
||||||
return undefined;
|
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -18,11 +18,6 @@ export interface LabPreviewFeaturesResponse {
|
|||||||
features: LabPreviewFeature[];
|
features: LabPreviewFeature[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch all lab features
|
|
||||||
* @param hass - The Home Assistant instance
|
|
||||||
* @returns A promise to fetch the lab features
|
|
||||||
*/
|
|
||||||
export const fetchLabFeatures = async (
|
export const fetchLabFeatures = async (
|
||||||
hass: HomeAssistant
|
hass: HomeAssistant
|
||||||
): Promise<LabPreviewFeature[]> => {
|
): Promise<LabPreviewFeature[]> => {
|
||||||
@@ -32,15 +27,6 @@ export const fetchLabFeatures = async (
|
|||||||
return response.features;
|
return response.features;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a specific lab feature
|
|
||||||
* @param hass - The Home Assistant instance
|
|
||||||
* @param domain - The domain of the lab feature
|
|
||||||
* @param preview_feature - The preview feature of the lab feature
|
|
||||||
* @param enabled - Whether the lab feature is enabled
|
|
||||||
* @param create_backup - Whether to create a backup of the lab feature
|
|
||||||
* @returns A promise to update the lab feature
|
|
||||||
*/
|
|
||||||
export const labsUpdatePreviewFeature = (
|
export const labsUpdatePreviewFeature = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
domain: string,
|
domain: string,
|
||||||
@@ -79,12 +65,6 @@ const subscribeLabUpdates = (
|
|||||||
"labs_updated"
|
"labs_updated"
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscribe to a collection of lab features
|
|
||||||
* @param conn - The connection to the Home Assistant instance
|
|
||||||
* @param onChange - The function to call when the lab features change
|
|
||||||
* @returns The unsubscribe function
|
|
||||||
*/
|
|
||||||
export const subscribeLabFeatures = (
|
export const subscribeLabFeatures = (
|
||||||
conn: Connection,
|
conn: Connection,
|
||||||
onChange: (features: LabPreviewFeature[]) => void
|
onChange: (features: LabPreviewFeature[]) => void
|
||||||
@@ -96,23 +76,3 @@ export const subscribeLabFeatures = (
|
|||||||
conn,
|
conn,
|
||||||
onChange
|
onChange
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 identifier
|
|
||||||
* @param onChange - The function to call when the lab feature changes
|
|
||||||
* @returns A promise that resolves to the unsubscribe function
|
|
||||||
*/
|
|
||||||
export const subscribeLabFeature = (
|
|
||||||
conn: Connection,
|
|
||||||
domain: string,
|
|
||||||
previewFeature: string,
|
|
||||||
onChange: (feature: LabPreviewFeature) => void
|
|
||||||
): Promise<() => void> =>
|
|
||||||
conn.subscribeMessage<LabPreviewFeature>(onChange, {
|
|
||||||
type: "labs/subscribe",
|
|
||||||
domain,
|
|
||||||
preview_feature: previewFeature,
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -220,12 +220,12 @@ const tryDescribeAction = <T extends ActionType>(
|
|||||||
if (config.action) {
|
if (config.action) {
|
||||||
const [domain, serviceName] = config.action.split(".", 2);
|
const [domain, serviceName] = config.action.split(".", 2);
|
||||||
const descriptionPlaceholders =
|
const descriptionPlaceholders =
|
||||||
hass.services[domain]?.[serviceName]?.description_placeholders;
|
hass.services[domain][serviceName].description_placeholders;
|
||||||
const service =
|
const service =
|
||||||
hass.localize(
|
hass.localize(
|
||||||
`component.${domain}.services.${serviceName}.name`,
|
`component.${domain}.services.${serviceName}.name`,
|
||||||
descriptionPlaceholders
|
descriptionPlaceholders
|
||||||
) || hass.services[domain]?.[serviceName]?.name;
|
) || hass.services[domain][serviceName]?.name;
|
||||||
|
|
||||||
if (config.metadata) {
|
if (config.metadata) {
|
||||||
return hass.localize(
|
return hass.localize(
|
||||||
|
|||||||
@@ -82,12 +82,6 @@ export interface WeatherEntity extends HassEntityBase {
|
|||||||
attributes: WeatherEntityAttributes;
|
attributes: WeatherEntityAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WEATHER_TEMPERATURE_ATTRIBUTES = new Set<string>([
|
|
||||||
"temperature",
|
|
||||||
"apparent_temperature",
|
|
||||||
"dew_point",
|
|
||||||
]);
|
|
||||||
|
|
||||||
export const weatherSVGs = new Set<string>([
|
export const weatherSVGs = new Set<string>([
|
||||||
"clear-night",
|
"clear-night",
|
||||||
"cloudy",
|
"cloudy",
|
||||||
@@ -262,15 +256,9 @@ export const getWeatherUnit = (
|
|||||||
export const getSecondaryWeatherAttribute = (
|
export const getSecondaryWeatherAttribute = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
stateObj: WeatherEntity,
|
stateObj: WeatherEntity,
|
||||||
forecast: ForecastAttribute[],
|
forecast: ForecastAttribute[]
|
||||||
temperatureFractionDigits?: number
|
|
||||||
): TemplateResult | undefined => {
|
): TemplateResult | undefined => {
|
||||||
const extrema = getWeatherExtrema(
|
const extrema = getWeatherExtrema(hass, stateObj, forecast);
|
||||||
hass,
|
|
||||||
stateObj,
|
|
||||||
forecast,
|
|
||||||
temperatureFractionDigits
|
|
||||||
);
|
|
||||||
|
|
||||||
if (extrema) {
|
if (extrema) {
|
||||||
return extrema;
|
return extrema;
|
||||||
@@ -310,8 +298,7 @@ export const getSecondaryWeatherAttribute = (
|
|||||||
const getWeatherExtrema = (
|
const getWeatherExtrema = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
stateObj: WeatherEntity,
|
stateObj: WeatherEntity,
|
||||||
forecast: ForecastAttribute[],
|
forecast: ForecastAttribute[]
|
||||||
temperatureFractionDigits?: number
|
|
||||||
): TemplateResult | undefined => {
|
): TemplateResult | undefined => {
|
||||||
if (!forecast?.length) {
|
if (!forecast?.length) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -326,22 +313,13 @@ const getWeatherExtrema = (
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!tempHigh || fc.temperature > tempHigh) {
|
if (!tempHigh || fc.temperature > tempHigh) {
|
||||||
tempHigh =
|
tempHigh = fc.temperature;
|
||||||
temperatureFractionDigits === undefined
|
|
||||||
? fc.temperature
|
|
||||||
: round(fc.temperature, temperatureFractionDigits);
|
|
||||||
}
|
}
|
||||||
if (fc.templow !== undefined && (!tempLow || fc.templow < tempLow)) {
|
if (!tempLow || (fc.templow && fc.templow < tempLow)) {
|
||||||
tempLow =
|
tempLow = fc.templow;
|
||||||
temperatureFractionDigits === undefined
|
|
||||||
? fc.templow
|
|
||||||
: round(fc.templow, temperatureFractionDigits);
|
|
||||||
}
|
}
|
||||||
if (!fc.templow && (!tempLow || fc.temperature < tempLow)) {
|
if (!fc.templow && (!tempLow || fc.temperature < tempLow)) {
|
||||||
tempLow =
|
tempLow = fc.temperature;
|
||||||
temperatureFractionDigits === undefined
|
|
||||||
? fc.temperature
|
|
||||||
: round(fc.temperature, temperatureFractionDigits);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,8 @@ import {
|
|||||||
import type { HassEntity } from "home-assistant-js-websocket";
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import type { PropertyValues } from "lit";
|
import type { PropertyValues } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { cache } from "lit/directives/cache";
|
import { cache } from "lit/directives/cache";
|
||||||
import { classMap } from "lit/directives/class-map";
|
|
||||||
import { keyed } from "lit/directives/keyed";
|
import { keyed } from "lit/directives/keyed";
|
||||||
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
|
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
@@ -51,12 +50,7 @@ import { lightSupportsFavoriteColors } from "../../data/light";
|
|||||||
import type { ItemType } from "../../data/search";
|
import type { ItemType } from "../../data/search";
|
||||||
import { SearchableDomains } from "../../data/search";
|
import { SearchableDomains } from "../../data/search";
|
||||||
import { getSensorNumericDeviceClasses } from "../../data/sensor";
|
import { getSensorNumericDeviceClasses } from "../../data/sensor";
|
||||||
import { ScrollableFadeMixin } from "../../mixins/scrollable-fade-mixin";
|
import { haStyleDialog, haStyleDialogFixedTop } from "../../resources/styles";
|
||||||
import {
|
|
||||||
haStyleDialog,
|
|
||||||
haStyleDialogFixedTop,
|
|
||||||
haStyleScrollbar,
|
|
||||||
} from "../../resources/styles";
|
|
||||||
import "../../state-summary/state-card-content";
|
import "../../state-summary/state-card-content";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import {
|
import {
|
||||||
@@ -102,15 +96,13 @@ declare global {
|
|||||||
const DEFAULT_VIEW: View = "info";
|
const DEFAULT_VIEW: View = "info";
|
||||||
|
|
||||||
@customElement("ha-more-info-dialog")
|
@customElement("ha-more-info-dialog")
|
||||||
export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
|
export class MoreInfoDialog extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ type: Boolean, reflect: true }) public large = false;
|
@property({ type: Boolean, reflect: true }) public large = false;
|
||||||
|
|
||||||
@state() private _parentEntityIds: string[] = [];
|
@state() private _parentEntityIds: string[] = [];
|
||||||
|
|
||||||
@query(".content") private _contentElement?: HTMLDivElement;
|
|
||||||
|
|
||||||
@state() private _entityId?: string | null;
|
@state() private _entityId?: string | null;
|
||||||
|
|
||||||
@state() private _data?: Record<string, any>;
|
@state() private _data?: Record<string, any>;
|
||||||
@@ -129,12 +121,6 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
|
|||||||
|
|
||||||
@state() private _sensorNumericDeviceClasses?: string[] = [];
|
@state() private _sensorNumericDeviceClasses?: string[] = [];
|
||||||
|
|
||||||
protected scrollFadeThreshold = 24;
|
|
||||||
|
|
||||||
protected get scrollableElement(): HTMLElement | null {
|
|
||||||
return this._contentElement || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public showDialog(params: MoreInfoDialogParams) {
|
public showDialog(params: MoreInfoDialogParams) {
|
||||||
this._entityId = params.entityId;
|
this._entityId = params.entityId;
|
||||||
if (!this._entityId) {
|
if (!this._entityId) {
|
||||||
@@ -316,9 +302,7 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _goToAddEntityTo(ev) {
|
private _goToAddEntityTo(ev) {
|
||||||
// Only check for request-selected events (from menu items), not regular clicks (from icon button)
|
if (!shouldHandleRequestSelectedEvent(ev)) return;
|
||||||
if (ev.type === "request-selected" && !shouldHandleRequestSelectedEvent(ev))
|
|
||||||
return;
|
|
||||||
this._setView("add_to");
|
this._setView("add_to");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,17 +550,6 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
|
|||||||
: nothing}
|
: nothing}
|
||||||
</ha-button-menu>
|
</ha-button-menu>
|
||||||
`
|
`
|
||||||
: !__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}
|
: nothing}
|
||||||
`
|
`
|
||||||
: isSpecificInitialView
|
: isSpecificInitialView
|
||||||
@@ -608,17 +581,11 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
|
|||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</ha-dialog-header>
|
</ha-dialog-header>
|
||||||
<div
|
|
||||||
class=${classMap({
|
|
||||||
"content-wrapper": true,
|
|
||||||
"settings-view": this._currView === "settings",
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
${keyed(
|
${keyed(
|
||||||
this._entityId,
|
this._entityId,
|
||||||
html`
|
html`
|
||||||
<div
|
<div
|
||||||
class="content ha-scrollbar"
|
class="content"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
dialogInitialFocus
|
dialogInitialFocus
|
||||||
@show-child-view=${this._showChildView}
|
@show-child-view=${this._showChildView}
|
||||||
@@ -686,8 +653,6 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
${this.renderScrollableFades()}
|
|
||||||
</div>
|
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -742,31 +707,18 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
|
|||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [
|
||||||
...super.styles,
|
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
haStyleDialogFixedTop,
|
haStyleDialogFixedTop,
|
||||||
haStyleScrollbar,
|
|
||||||
css`
|
css`
|
||||||
ha-dialog {
|
ha-dialog {
|
||||||
--dialog-content-padding: 0;
|
--dialog-content-padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-wrapper {
|
.content {
|
||||||
flex: 1 1 auto;
|
|
||||||
min-height: 0;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
outline: none;
|
outline: none;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-wrapper.settings-view .fade-bottom {
|
|
||||||
bottom: var(--ha-space-18);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.child-view {
|
.child-view {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import "../../components/ha-alert";
|
|||||||
import "../../components/ha-expansion-panel";
|
import "../../components/ha-expansion-panel";
|
||||||
import "../../components/ha-fade-in";
|
import "../../components/ha-fade-in";
|
||||||
import "../../components/ha-icon-next";
|
import "../../components/ha-icon-next";
|
||||||
import "../../components/ha-adaptive-dialog";
|
import "../../components/ha-wa-dialog";
|
||||||
import "../../components/ha-md-list";
|
import "../../components/ha-md-list";
|
||||||
import "../../components/ha-md-list-item";
|
import "../../components/ha-md-list-item";
|
||||||
import "../../components/ha-spinner";
|
import "../../components/ha-spinner";
|
||||||
@@ -109,7 +109,7 @@ class DialogRestart extends LitElement {
|
|||||||
const dialogTitle = this.hass.localize("ui.dialogs.restart.heading");
|
const dialogTitle = this.hass.localize("ui.dialogs.restart.heading");
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-adaptive-dialog
|
<ha-wa-dialog
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.open=${this._dialogOpen}
|
.open=${this._dialogOpen}
|
||||||
header-title=${dialogTitle}
|
header-title=${dialogTitle}
|
||||||
@@ -257,7 +257,7 @@ class DialogRestart extends LitElement {
|
|||||||
</ha-expansion-panel>
|
</ha-expansion-panel>
|
||||||
`}
|
`}
|
||||||
</div>
|
</div>
|
||||||
</ha-adaptive-dialog>
|
</ha-wa-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,7 +405,7 @@ class DialogRestart extends LitElement {
|
|||||||
haStyle,
|
haStyle,
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
ha-adaptive-dialog {
|
ha-wa-dialog {
|
||||||
--dialog-content-padding: 0;
|
--dialog-content-padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import "@material/mwc-linear-progress/mwc-linear-progress";
|
import "@material/mwc-linear-progress/mwc-linear-progress";
|
||||||
import { mdiDotsVertical, mdiRestart } from "@mdi/js";
|
import { mdiClose, mdiDotsVertical, mdiRestart } from "@mdi/js";
|
||||||
import { css, html, LitElement, type TemplateResult } from "lit";
|
import { css, html, LitElement, nothing, type TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import "../../components/ha-alert";
|
import "../../components/ha-alert";
|
||||||
import "../../components/ha-button";
|
import "../../components/ha-dialog-header";
|
||||||
import "../../components/ha-dialog-footer";
|
|
||||||
import "../../components/ha-fade-in";
|
import "../../components/ha-fade-in";
|
||||||
import "../../components/ha-icon-button";
|
import "../../components/ha-icon-button";
|
||||||
import "../../components/ha-items-display-editor";
|
import "../../components/ha-items-display-editor";
|
||||||
@@ -15,8 +14,9 @@ import type {
|
|||||||
DisplayValue,
|
DisplayValue,
|
||||||
} from "../../components/ha-items-display-editor";
|
} from "../../components/ha-items-display-editor";
|
||||||
import "../../components/ha-md-button-menu";
|
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-md-menu-item";
|
||||||
import "../../components/ha-wa-dialog";
|
|
||||||
import { computePanels } from "../../components/ha-sidebar";
|
import { computePanels } from "../../components/ha-sidebar";
|
||||||
import "../../components/ha-spinner";
|
import "../../components/ha-spinner";
|
||||||
import "../../components/ha-svg-icon";
|
import "../../components/ha-svg-icon";
|
||||||
@@ -40,6 +40,8 @@ class DialogEditSidebar extends LitElement {
|
|||||||
|
|
||||||
@state() private _open = false;
|
@state() private _open = false;
|
||||||
|
|
||||||
|
@query("ha-md-dialog") private _dialog?: HaMdDialog;
|
||||||
|
|
||||||
@state() private _order?: string[];
|
@state() private _order?: string[];
|
||||||
|
|
||||||
@state() private _hidden?: string[];
|
@state() private _hidden?: string[];
|
||||||
@@ -53,6 +55,7 @@ class DialogEditSidebar extends LitElement {
|
|||||||
|
|
||||||
public async showDialog(): Promise<void> {
|
public async showDialog(): Promise<void> {
|
||||||
this._open = true;
|
this._open = true;
|
||||||
|
|
||||||
this._getData();
|
this._getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +87,7 @@ class DialogEditSidebar extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public closeDialog(): void {
|
public closeDialog(): void {
|
||||||
this._open = false;
|
this._dialog?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _panels = memoizeOne((panels: HomeAssistant["panels"]) =>
|
private _panels = memoizeOne((panels: HomeAssistant["panels"]) =>
|
||||||
@@ -164,20 +167,29 @@ class DialogEditSidebar extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
|
if (!this._open) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
const dialogTitle = this.hass.localize("ui.sidebar.edit_sidebar");
|
const dialogTitle = this.hass.localize("ui.sidebar.edit_sidebar");
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-wa-dialog
|
<ha-md-dialog open @closed=${this._dialogClosed}>
|
||||||
.hass=${this.hass}
|
<ha-dialog-header slot="headline">
|
||||||
.open=${this._open}
|
<ha-icon-button
|
||||||
header-title=${dialogTitle}
|
slot="navigationIcon"
|
||||||
header-subtitle=${!this._migrateToUserData
|
.label=${this.hass.localize("ui.common.close") ?? "Close"}
|
||||||
? this.hass.localize("ui.sidebar.edit_subtitle")
|
.path=${mdiClose}
|
||||||
: ""}
|
@click=${this.closeDialog}
|
||||||
@closed=${this._dialogClosed}
|
></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
|
<ha-md-button-menu
|
||||||
slot="headerActionItems"
|
slot="actionItems"
|
||||||
positioning="popover"
|
positioning="popover"
|
||||||
anchor-corner="end-end"
|
anchor-corner="end-end"
|
||||||
menu-corner="start-end"
|
menu-corner="start-end"
|
||||||
@@ -192,24 +204,20 @@ class DialogEditSidebar extends LitElement {
|
|||||||
${this.hass.localize("ui.sidebar.reset_to_defaults")}
|
${this.hass.localize("ui.sidebar.reset_to_defaults")}
|
||||||
</ha-md-menu-item>
|
</ha-md-menu-item>
|
||||||
</ha-md-button-menu>
|
</ha-md-button-menu>
|
||||||
<div class="content">${this._renderContent()}</div>
|
</ha-dialog-header>
|
||||||
<ha-dialog-footer slot="footer">
|
<div slot="content" class="content">${this._renderContent()}</div>
|
||||||
<ha-button
|
<div slot="actions">
|
||||||
slot="secondaryAction"
|
<ha-button appearance="plain" @click=${this.closeDialog}>
|
||||||
appearance="plain"
|
|
||||||
@click=${this.closeDialog}
|
|
||||||
>
|
|
||||||
${this.hass.localize("ui.common.cancel")}
|
${this.hass.localize("ui.common.cancel")}
|
||||||
</ha-button>
|
</ha-button>
|
||||||
<ha-button
|
<ha-button
|
||||||
slot="primaryAction"
|
|
||||||
.disabled=${!this._order || !this._hidden}
|
.disabled=${!this._order || !this._hidden}
|
||||||
@click=${this._save}
|
@click=${this._save}
|
||||||
>
|
>
|
||||||
${this.hass.localize("ui.common.save")}
|
${this.hass.localize("ui.common.save")}
|
||||||
</ha-button>
|
</ha-button>
|
||||||
</ha-dialog-footer>
|
</div>
|
||||||
</ha-wa-dialog>
|
</ha-md-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,13 +272,15 @@ class DialogEditSidebar extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
ha-wa-dialog {
|
ha-md-dialog {
|
||||||
|
min-width: 600px;
|
||||||
max-height: 90%;
|
max-height: 90%;
|
||||||
--dialog-content-padding: var(--ha-space-2) var(--ha-space-6);
|
--dialog-content-padding: 8px 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media all and (max-width: 580px), all and (max-height: 500px) {
|
@media all and (max-width: 600px), all and (max-height: 500px) {
|
||||||
ha-wa-dialog {
|
ha-md-dialog {
|
||||||
|
--md-dialog-container-shape: 0;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import "../components/ha-svg-icon";
|
|||||||
import "../components/ha-tab";
|
import "../components/ha-tab";
|
||||||
import { haStyleScrollbar } from "../resources/styles";
|
import { haStyleScrollbar } from "../resources/styles";
|
||||||
import type { HomeAssistant, Route } from "../types";
|
import type { HomeAssistant, Route } from "../types";
|
||||||
import { withViewTransition } from "../common/util/view-transition";
|
|
||||||
|
|
||||||
export interface PageNavigation {
|
export interface PageNavigation {
|
||||||
path: string;
|
path: string;
|
||||||
@@ -113,11 +112,9 @@ class HassTabsSubpage extends LitElement {
|
|||||||
|
|
||||||
public willUpdate(changedProperties: PropertyValues) {
|
public willUpdate(changedProperties: PropertyValues) {
|
||||||
if (changedProperties.has("route")) {
|
if (changedProperties.has("route")) {
|
||||||
withViewTransition(() => {
|
|
||||||
this._activeTab = this.tabs.find((tab) =>
|
this._activeTab = this.tabs.find((tab) =>
|
||||||
`${this.route.prefix}${this.route.path}`.includes(tab.path)
|
`${this.route.prefix}${this.route.path}`.includes(tab.path)
|
||||||
);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
super.willUpdate(changedProperties);
|
super.willUpdate(changedProperties);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,27 +13,19 @@ import type { Constructor } from "../types";
|
|||||||
const stylesArray = (styles?: CSSResultGroup | CSSResultGroup[]) =>
|
const stylesArray = (styles?: CSSResultGroup | CSSResultGroup[]) =>
|
||||||
styles === undefined ? [] : Array.isArray(styles) ? styles : [styles];
|
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>>(
|
export const ScrollableFadeMixin = <T extends Constructor<LitElement>>(
|
||||||
superClass: T
|
superClass: T
|
||||||
) => {
|
) => {
|
||||||
class ScrollableFadeClass extends superClass {
|
class ScrollableFadeClass extends superClass {
|
||||||
/** Whether content has scrolled past the threshold. Controls top fade visibility. */
|
|
||||||
@state() protected _contentScrolled = false;
|
@state() protected _contentScrolled = false;
|
||||||
|
|
||||||
/** Whether content extends beyond the viewport. Controls bottom fade visibility. */
|
|
||||||
@state() protected _contentScrollable = false;
|
@state() protected _contentScrollable = false;
|
||||||
|
|
||||||
private _scrollTarget?: HTMLElement | null;
|
private _scrollTarget?: HTMLElement | null;
|
||||||
|
|
||||||
private _onScroll = (ev: Event) => {
|
private _onScroll = (ev: Event) => {
|
||||||
const target = ev.currentTarget as HTMLElement;
|
const target = ev.currentTarget as HTMLElement;
|
||||||
this._contentScrolled =
|
this._contentScrolled = (target.scrollTop ?? 0) > 0;
|
||||||
(target.scrollTop ?? 0) > this.scrollFadeThreshold;
|
|
||||||
this._updateScrollableState(target);
|
this._updateScrollableState(target);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,26 +39,15 @@ 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 =
|
private static readonly DEFAULT_SCROLLABLE_ELEMENT: HTMLElement | null =
|
||||||
null;
|
null;
|
||||||
|
|
||||||
/**
|
protected get scrollFadeSafeAreaPadding() {
|
||||||
* Element to observe for scroll and resize events. Override with a getter to specify target.
|
return ScrollableFadeClass.DEFAULT_SAFE_AREA_PADDING;
|
||||||
* Kept as a getter to allow subclasses to return query results.
|
}
|
||||||
*/
|
|
||||||
protected get scrollableElement(): HTMLElement | null {
|
protected get scrollableElement(): HTMLElement | null {
|
||||||
return ScrollableFadeClass.DEFAULT_SCROLLABLE_ELEMENT;
|
return ScrollableFadeClass.DEFAULT_SCROLLABLE_ELEMENT;
|
||||||
}
|
}
|
||||||
@@ -86,11 +67,6 @@ export const ScrollableFadeMixin = <T extends Constructor<LitElement>>(
|
|||||||
super.disconnectedCallback();
|
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 {
|
protected renderScrollableFades(rounded = false): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
@@ -134,6 +110,7 @@ export const ScrollableFadeMixin = <T extends Constructor<LitElement>>(
|
|||||||
transparent
|
transparent
|
||||||
);
|
);
|
||||||
border-radius: var(--ha-border-radius-square);
|
border-radius: var(--ha-border-radius-square);
|
||||||
|
z-index: 100;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
.fade-top {
|
.fade-top {
|
||||||
|
|||||||
@@ -81,11 +81,8 @@ class DialogAreasFloorsOrder extends LitElement {
|
|||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasFloors = this._hierarchy.floors.length > 0;
|
|
||||||
const dialogTitle = this.hass.localize(
|
const dialogTitle = this.hass.localize(
|
||||||
hasFloors
|
"ui.panel.config.areas.dialog.reorder_title"
|
||||||
? "ui.panel.config.areas.dialog.reorder_floors_areas_title"
|
|
||||||
: "ui.panel.config.areas.dialog.reorder_areas_title"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
@@ -175,7 +172,7 @@ class DialogAreasFloorsOrder extends LitElement {
|
|||||||
? html`<div class="floor-header">
|
? html`<div class="floor-header">
|
||||||
<span class="floor-name">
|
<span class="floor-name">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.areas.dialog.other_areas"
|
"ui.panel.config.areas.dialog.unassigned_areas"
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>`
|
</div>`
|
||||||
@@ -421,6 +418,7 @@ class DialogAreasFloorsOrder extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.floor.unassigned {
|
.floor.unassigned {
|
||||||
|
border-style: dashed;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -175,12 +175,21 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
has-fab
|
has-fab
|
||||||
>
|
>
|
||||||
|
<ha-button-menu slot="toolbar-icon">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="toolbar-icon"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.help")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
.path=${mdiHelpCircle}
|
.path=${mdiDotsVertical}
|
||||||
@click=${this._showHelp}
|
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
<ha-list-item graphic="icon" @click=${this._showReorderDialog}>
|
||||||
|
<ha-svg-icon .path=${mdiSort} slot="graphic"></ha-svg-icon>
|
||||||
|
${this.hass.localize("ui.panel.config.areas.picker.reorder")}
|
||||||
|
</ha-list-item>
|
||||||
|
<ha-list-item graphic="icon" @click=${this._showHelp}>
|
||||||
|
<ha-svg-icon .path=${mdiHelpCircle} slot="graphic"></ha-svg-icon>
|
||||||
|
${this.hass.localize("ui.common.help")}
|
||||||
|
</ha-list-item>
|
||||||
|
</ha-button-menu>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="floors">
|
<div class="floors">
|
||||||
${this._hierarchy.floors.map(({ areas, id }) => {
|
${this._hierarchy.floors.map(({ areas, id }) => {
|
||||||
@@ -204,16 +213,6 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
slot="trigger"
|
slot="trigger"
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-list-item graphic="icon"
|
|
||||||
><ha-svg-icon
|
|
||||||
.path=${mdiSort}
|
|
||||||
slot="graphic"
|
|
||||||
></ha-svg-icon
|
|
||||||
>${this.hass.localize(
|
|
||||||
"ui.panel.config.areas.picker.reorder"
|
|
||||||
)}</ha-list-item
|
|
||||||
>
|
|
||||||
<li divider role="separator"></li>
|
|
||||||
<ha-list-item graphic="icon"
|
<ha-list-item graphic="icon"
|
||||||
><ha-svg-icon
|
><ha-svg-icon
|
||||||
.path=${mdiPencil}
|
.path=${mdiPencil}
|
||||||
@@ -267,30 +266,9 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<h2>
|
<h2>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
this._hierarchy.floors.length
|
"ui.panel.config.areas.picker.unassigned_areas"
|
||||||
? "ui.panel.config.areas.picker.other_areas"
|
|
||||||
: "ui.panel.config.areas.picker.header"
|
|
||||||
)}
|
)}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="actions">
|
|
||||||
<ha-button-menu
|
|
||||||
@action=${this._handleUnassignedAreasAction}
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
|
||||||
slot="trigger"
|
|
||||||
.path=${mdiDotsVertical}
|
|
||||||
></ha-icon-button>
|
|
||||||
<ha-list-item graphic="icon"
|
|
||||||
><ha-svg-icon
|
|
||||||
.path=${mdiSort}
|
|
||||||
slot="graphic"
|
|
||||||
></ha-svg-icon
|
|
||||||
>${this.hass.localize(
|
|
||||||
"ui.panel.config.areas.picker.reorder"
|
|
||||||
)}</ha-list-item
|
|
||||||
>
|
|
||||||
</ha-button-menu>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<ha-sortable
|
<ha-sortable
|
||||||
handle-selector="a"
|
handle-selector="a"
|
||||||
@@ -537,23 +515,14 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
const floor = (ev.currentTarget as any).floor;
|
const floor = (ev.currentTarget as any).floor;
|
||||||
switch (ev.detail.index) {
|
switch (ev.detail.index) {
|
||||||
case 0:
|
case 0:
|
||||||
this._showReorderDialog();
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
this._editFloor(floor);
|
this._editFloor(floor);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 1:
|
||||||
this._deleteFloor(floor);
|
this._deleteFloor(floor);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleUnassignedAreasAction(ev: CustomEvent<ActionDetail>) {
|
|
||||||
if (ev.detail.index === 0) {
|
|
||||||
this._showReorderDialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _createFloor() {
|
private _createFloor() {
|
||||||
this._openFloorDialog();
|
this._openFloorDialog();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
|
||||||
import { consume } from "@lit/context";
|
import { consume } from "@lit/context";
|
||||||
import {
|
import {
|
||||||
mdiAlertCircleCheck,
|
mdiAlertCircleCheck,
|
||||||
@@ -33,11 +32,11 @@ import { copyToClipboard } from "../../../../common/util/copy-clipboard";
|
|||||||
import "../../../../components/ha-automation-row";
|
import "../../../../components/ha-automation-row";
|
||||||
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-dropdown";
|
|
||||||
import "../../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../../components/ha-dropdown-item";
|
|
||||||
import "../../../../components/ha-expansion-panel";
|
import "../../../../components/ha-expansion-panel";
|
||||||
import "../../../../components/ha-icon-button";
|
import "../../../../components/ha-icon-button";
|
||||||
|
import "../../../../components/ha-md-button-menu";
|
||||||
|
import "../../../../components/ha-md-divider";
|
||||||
|
import "../../../../components/ha-md-menu-item";
|
||||||
import "../../../../components/ha-service-icon";
|
import "../../../../components/ha-service-icon";
|
||||||
import "../../../../components/ha-tooltip";
|
import "../../../../components/ha-tooltip";
|
||||||
import {
|
import {
|
||||||
@@ -289,12 +288,15 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
</ha-tooltip>`
|
</ha-tooltip>`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-dropdown
|
<ha-md-button-menu
|
||||||
|
quick
|
||||||
slot="icons"
|
slot="icons"
|
||||||
@click=${preventDefaultStopPropagation}
|
@click=${preventDefaultStopPropagation}
|
||||||
@keydown=${stopPropagation}
|
@keydown=${stopPropagation}
|
||||||
@wa-select=${this._handleDropdownSelect}
|
@closed=${stopPropagation}
|
||||||
placement="bottom-end"
|
positioning="fixed"
|
||||||
|
anchor-corner="end-end"
|
||||||
|
menu-corner="start-end"
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
@@ -302,24 +304,30 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
|
||||||
<ha-dropdown-item value="run">
|
<ha-md-menu-item .clickAction=${this._runAction}>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlay}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiPlay}></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
this.hass.localize("ui.panel.config.automation.editor.actions.run")
|
this.hass.localize("ui.panel.config.automation.editor.actions.run")
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item value="rename" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
<ha-svg-icon slot="icon" .path=${mdiRenameBox}></ha-svg-icon>
|
.clickAction=${this._renameAction}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.rename"
|
"ui.panel.config.automation.editor.triggers.rename"
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<wa-divider></wa-divider>
|
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||||
<ha-dropdown-item value="duplicate" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._duplicateAction}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${mdiPlusCircleMultipleOutline}
|
.path=${mdiPlusCircleMultipleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
|
|
||||||
@@ -328,10 +336,13 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
"ui.panel.config.automation.editor.actions.duplicate"
|
"ui.panel.config.automation.editor.actions.duplicate"
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<ha-dropdown-item value="copy" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCopy}></ha-svg-icon>
|
.clickAction=${this._copyAction}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.copy"
|
"ui.panel.config.automation.editor.triggers.copy"
|
||||||
@@ -340,6 +351,7 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -350,10 +362,13 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
<span>C</span>
|
<span>C</span>
|
||||||
</span>`
|
</span>`
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<ha-dropdown-item value="cut" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCut}></ha-svg-icon>
|
.clickAction=${this._cutAction}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.cut"
|
"ui.panel.config.automation.editor.triggers.cut"
|
||||||
@@ -362,6 +377,7 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -372,48 +388,51 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
<span>X</span>
|
<span>X</span>
|
||||||
</span>`
|
</span>`
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
${!this.optionsInSidebar
|
${!this.optionsInSidebar
|
||||||
? html`
|
? html`
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="move_up"
|
.clickAction=${this._moveUp}
|
||||||
.disabled=${this.disabled || !!this.first}
|
.disabled=${this.disabled || !!this.first}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.move_up"
|
"ui.panel.config.automation.editor.move_up"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiArrowUp}></ha-svg-icon
|
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
||||||
></ha-dropdown-item>
|
></ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="move_down"
|
.clickAction=${this._moveDown}
|
||||||
.disabled=${this.disabled || !!this.last}
|
.disabled=${this.disabled || !!this.last}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.move_down"
|
"ui.panel.config.automation.editor.move_down"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiArrowDown}></ha-svg-icon
|
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
||||||
></ha-dropdown-item>
|
></ha-md-menu-item>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="toggle_yaml_mode"
|
.clickAction=${this._toggleYamlMode}
|
||||||
.disabled=${!this._uiModeAvailable || !!this._warnings}
|
.disabled=${!this._uiModeAvailable || !!this._warnings}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
|
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<wa-divider></wa-divider>
|
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||||
|
|
||||||
<ha-dropdown-item value="disable" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._onDisable}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${this.action.enabled === false
|
.path=${this.action.enabled === false
|
||||||
? mdiPlayCircleOutline
|
? mdiPlayCircleOutline
|
||||||
: mdiStopCircleOutline}
|
: mdiStopCircleOutline}
|
||||||
@@ -424,15 +443,15 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
`ui.panel.config.automation.editor.actions.${this.action.enabled === false ? "enable" : "disable"}`
|
`ui.panel.config.automation.editor.actions.${this.action.enabled === false ? "enable" : "disable"}`
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="delete"
|
class="warning"
|
||||||
variant="danger"
|
.clickAction=${this._onDelete}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class="warning"
|
class="warning"
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${mdiDelete}
|
.path=${mdiDelete}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
|
|
||||||
@@ -444,6 +463,7 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -458,8 +478,8 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
>
|
>
|
||||||
</span>`
|
</span>`
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
</ha-dropdown>
|
</ha-md-button-menu>
|
||||||
|
|
||||||
${!this.optionsInSidebar
|
${!this.optionsInSidebar
|
||||||
? html`${this._warnings
|
? html`${this._warnings
|
||||||
@@ -870,47 +890,6 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
this._automationRowElement?.focus();
|
this._automationRowElement?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "run":
|
|
||||||
this._runAction();
|
|
||||||
break;
|
|
||||||
case "rename":
|
|
||||||
this._renameAction();
|
|
||||||
break;
|
|
||||||
case "duplicate":
|
|
||||||
this._duplicateAction();
|
|
||||||
break;
|
|
||||||
case "copy":
|
|
||||||
this._copyAction();
|
|
||||||
break;
|
|
||||||
case "cut":
|
|
||||||
this._cutAction();
|
|
||||||
break;
|
|
||||||
case "move_up":
|
|
||||||
this._moveUp();
|
|
||||||
break;
|
|
||||||
case "move_down":
|
|
||||||
this._moveDown();
|
|
||||||
break;
|
|
||||||
case "toggle_yaml_mode":
|
|
||||||
this._toggleYamlMode(ev.target as HTMLElement);
|
|
||||||
break;
|
|
||||||
case "disable":
|
|
||||||
this._onDisable();
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
this._onDelete();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = [rowStyles, overflowStyles];
|
static styles = [rowStyles, overflowStyles];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,15 +64,8 @@ export class HaStopAction extends LitElement implements ActionElement {
|
|||||||
|
|
||||||
private _responseChanged(ev: Event) {
|
private _responseChanged(ev: Event) {
|
||||||
ev.stopPropagation();
|
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", {
|
fireEvent(this, "value-changed", {
|
||||||
value: newAction,
|
value: { ...this.action, response_variable: (ev.target as any).value },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ import {
|
|||||||
fetchIntegrationManifests,
|
fetchIntegrationManifests,
|
||||||
} from "../../../data/integration";
|
} from "../../../data/integration";
|
||||||
import type { LabelRegistryEntry } from "../../../data/label_registry";
|
import type { LabelRegistryEntry } from "../../../data/label_registry";
|
||||||
import { subscribeLabFeature } from "../../../data/labs";
|
import { subscribeLabFeatures } from "../../../data/labs";
|
||||||
import {
|
import {
|
||||||
TARGET_SEPARATOR,
|
TARGET_SEPARATOR,
|
||||||
getConditionsForTarget,
|
getConditionsForTarget,
|
||||||
@@ -228,7 +228,7 @@ class DialogAddAutomationElement
|
|||||||
|
|
||||||
private _unsub?: Promise<UnsubscribeFunc>;
|
private _unsub?: Promise<UnsubscribeFunc>;
|
||||||
|
|
||||||
private _unsubscribeLabFeatures?: Promise<UnsubscribeFunc>;
|
private _unsubscribeLabFeatures?: UnsubscribeFunc;
|
||||||
|
|
||||||
private _configEntryLookup: Record<string, ConfigEntry> = {};
|
private _configEntryLookup: Record<string, ConfigEntry> = {};
|
||||||
|
|
||||||
@@ -281,12 +281,15 @@ class DialogAddAutomationElement
|
|||||||
this._fetchManifests();
|
this._fetchManifests();
|
||||||
this._calculateUsedDomains();
|
this._calculateUsedDomains();
|
||||||
|
|
||||||
this._unsubscribeLabFeatures = subscribeLabFeature(
|
this._unsubscribeLabFeatures = subscribeLabFeatures(
|
||||||
this.hass.connection,
|
this.hass.connection,
|
||||||
"automation",
|
(features) => {
|
||||||
"new_triggers_conditions",
|
this._newTriggersAndConditions =
|
||||||
(feature) => {
|
features.find(
|
||||||
this._newTriggersAndConditions = feature.enabled;
|
(feature) =>
|
||||||
|
feature.domain === "automation" &&
|
||||||
|
feature.preview_feature === "new_triggers_conditions"
|
||||||
|
)?.enabled ?? false;
|
||||||
this._tab = this._newTriggersAndConditions ? "targets" : "groups";
|
this._tab = this._newTriggersAndConditions ? "targets" : "groups";
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -422,7 +425,7 @@ class DialogAddAutomationElement
|
|||||||
this._unsub = undefined;
|
this._unsub = undefined;
|
||||||
}
|
}
|
||||||
if (this._unsubscribeLabFeatures) {
|
if (this._unsubscribeLabFeatures) {
|
||||||
this._unsubscribeLabFeatures.then((unsub) => unsub());
|
this._unsubscribeLabFeatures();
|
||||||
this._unsubscribeLabFeatures = undefined;
|
this._unsubscribeLabFeatures = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -683,7 +686,6 @@ class DialogAddAutomationElement
|
|||||||
<ha-automation-add-items
|
<ha-automation-add-items
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.items=${this._getItems()}
|
.items=${this._getItems()}
|
||||||
.scrollable=${!this._narrow}
|
|
||||||
.error=${this._tab === "targets" && this._loadItemsError
|
.error=${this._tab === "targets" && this._loadItemsError
|
||||||
? this.hass.localize(
|
? this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.load_target_items_failed"
|
"ui.panel.config.automation.editor.load_target_items_failed"
|
||||||
@@ -2144,7 +2146,7 @@ class DialogAddAutomationElement
|
|||||||
min-height: 160px;
|
min-height: 160px;
|
||||||
}
|
}
|
||||||
.content.column ha-automation-add-from-target {
|
.content.column ha-automation-add-from-target {
|
||||||
overflow: clip;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-wa-dialog ha-automation-add-items {
|
ha-wa-dialog ha-automation-add-items {
|
||||||
|
|||||||
@@ -911,10 +911,6 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
|||||||
const services: Record<string, Level3Entries> = {};
|
const services: Record<string, Level3Entries> = {};
|
||||||
|
|
||||||
unassignedDevices.forEach(({ id: deviceId, entry_type }) => {
|
unassignedDevices.forEach(({ id: deviceId, entry_type }) => {
|
||||||
const device = this.devices[deviceId];
|
|
||||||
if (!device || device.disabled_by) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const deviceEntry = {
|
const deviceEntry = {
|
||||||
open: false,
|
open: false,
|
||||||
entities:
|
entities:
|
||||||
@@ -1016,10 +1012,6 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
|||||||
const devices: Record<string, Level3Entries> = {};
|
const devices: Record<string, Level3Entries> = {};
|
||||||
|
|
||||||
referenced_devices.forEach(({ id: deviceId }) => {
|
referenced_devices.forEach(({ id: deviceId }) => {
|
||||||
const device = this.devices[deviceId];
|
|
||||||
if (!device || device.disabled_by) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
devices[deviceId] = {
|
devices[deviceId] = {
|
||||||
open: false,
|
open: false,
|
||||||
entities:
|
entities:
|
||||||
|
|||||||
@@ -60,8 +60,6 @@ export class HaAutomationAddItems extends LitElement {
|
|||||||
@property({ type: Boolean, attribute: "tooltip-description" })
|
@property({ type: Boolean, attribute: "tooltip-description" })
|
||||||
public tooltipDescription = false;
|
public tooltipDescription = false;
|
||||||
|
|
||||||
@property({ type: Boolean, reflect: true }) scrollable = false;
|
|
||||||
|
|
||||||
@state() private _itemsScrolled = false;
|
@state() private _itemsScrolled = false;
|
||||||
|
|
||||||
@query(".items")
|
@query(".items")
|
||||||
@@ -262,12 +260,11 @@ export class HaAutomationAddItems extends LitElement {
|
|||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
:host([scrollable]) .items {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
.items {
|
.items {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow: auto;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.items.blank {
|
.items.blank {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
|
||||||
import { consume } from "@lit/context";
|
import { consume } from "@lit/context";
|
||||||
import {
|
import {
|
||||||
mdiAppleKeyboardCommand,
|
mdiAppleKeyboardCommand,
|
||||||
@@ -34,11 +33,11 @@ import "../../../../components/ha-automation-row";
|
|||||||
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-condition-icon";
|
import "../../../../components/ha-condition-icon";
|
||||||
import "../../../../components/ha-dropdown";
|
|
||||||
import "../../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../../components/ha-dropdown-item";
|
|
||||||
import "../../../../components/ha-expansion-panel";
|
import "../../../../components/ha-expansion-panel";
|
||||||
import "../../../../components/ha-icon-button";
|
import "../../../../components/ha-icon-button";
|
||||||
|
import "../../../../components/ha-md-button-menu";
|
||||||
|
import "../../../../components/ha-md-divider";
|
||||||
|
import "../../../../components/ha-md-menu-item";
|
||||||
import type {
|
import type {
|
||||||
AutomationClipboard,
|
AutomationClipboard,
|
||||||
Condition,
|
Condition,
|
||||||
@@ -195,12 +194,15 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
|
|
||||||
<slot name="icons" slot="icons"></slot>
|
<slot name="icons" slot="icons"></slot>
|
||||||
|
|
||||||
<ha-dropdown
|
<ha-md-button-menu
|
||||||
|
quick
|
||||||
slot="icons"
|
slot="icons"
|
||||||
@click=${preventDefaultStopPropagation}
|
@click=${preventDefaultStopPropagation}
|
||||||
@keydown=${stopPropagation}
|
@keydown=${stopPropagation}
|
||||||
@wa-select=${this._handleDropdownSelect}
|
@closed=${stopPropagation}
|
||||||
placement="bottom-end"
|
positioning="fixed"
|
||||||
|
anchor-corner="end-end"
|
||||||
|
menu-corner="start-end"
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
@@ -209,28 +211,34 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
>
|
>
|
||||||
</ha-icon-button>
|
</ha-icon-button>
|
||||||
|
|
||||||
<ha-dropdown-item value="test">
|
<ha-md-menu-item .clickAction=${this._testCondition}>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiFlask}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiFlask}></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.conditions.test"
|
"ui.panel.config.automation.editor.conditions.test"
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item value="rename" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
<ha-svg-icon slot="icon" .path=${mdiRenameBox}></ha-svg-icon>
|
.clickAction=${this._renameCondition}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.conditions.rename"
|
"ui.panel.config.automation.editor.conditions.rename"
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<wa-divider></wa-divider>
|
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||||
|
|
||||||
<ha-dropdown-item value="duplicate" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._duplicateCondition}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${mdiPlusCircleMultipleOutline}
|
.path=${mdiPlusCircleMultipleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
@@ -238,10 +246,13 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
"ui.panel.config.automation.editor.actions.duplicate"
|
"ui.panel.config.automation.editor.actions.duplicate"
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<ha-dropdown-item value="copy" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCopy}></ha-svg-icon
|
.clickAction=${this._copyCondition}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon
|
||||||
>${this._renderOverflowLabel(
|
>${this._renderOverflowLabel(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.copy"
|
"ui.panel.config.automation.editor.triggers.copy"
|
||||||
@@ -250,6 +261,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -260,10 +272,13 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
<span>C</span>
|
<span>C</span>
|
||||||
</span>`
|
</span>`
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<ha-dropdown-item value="cut" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCut}></ha-svg-icon
|
.clickAction=${this._cutCondition}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon
|
||||||
>${this._renderOverflowLabel(
|
>${this._renderOverflowLabel(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.cut"
|
"ui.panel.config.automation.editor.triggers.cut"
|
||||||
@@ -272,6 +287,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -282,45 +298,48 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
<span>X</span>
|
<span>X</span>
|
||||||
</span>`
|
</span>`
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
${!this.optionsInSidebar
|
${!this.optionsInSidebar
|
||||||
? html`
|
? html`
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="move_up"
|
.clickAction=${this._moveUp}
|
||||||
.disabled=${this.disabled || !!this.first}
|
.disabled=${this.disabled || this.first}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.move_up"
|
"ui.panel.config.automation.editor.move_up"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiArrowUp}></ha-svg-icon
|
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
||||||
></ha-dropdown-item>
|
></ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="move_down"
|
.clickAction=${this._moveDown}
|
||||||
.disabled=${this.disabled || !!this.last}
|
.disabled=${this.disabled || this.last}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.move_down"
|
"ui.panel.config.automation.editor.move_down"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiArrowDown}></ha-svg-icon
|
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
||||||
></ha-dropdown-item>
|
></ha-md-menu-item>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-dropdown-item value="toggle_yaml_mode">
|
<ha-md-menu-item .clickAction=${this._toggleYamlMode}>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
|
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<wa-divider></wa-divider>
|
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||||
|
|
||||||
<ha-dropdown-item value="disable" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._onDisable}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${this.condition.enabled === false
|
.path=${this.condition.enabled === false
|
||||||
? mdiPlayCircleOutline
|
? mdiPlayCircleOutline
|
||||||
: mdiStopCircleOutline}
|
: mdiStopCircleOutline}
|
||||||
@@ -331,15 +350,15 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
`ui.panel.config.automation.editor.actions.${this.condition.enabled === false ? "enable" : "disable"}`
|
`ui.panel.config.automation.editor.actions.${this.condition.enabled === false ? "enable" : "disable"}`
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
variant="danger"
|
class="warning"
|
||||||
value="delete"
|
.clickAction=${this._onDelete}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class="warning"
|
class="warning"
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${mdiDelete}
|
.path=${mdiDelete}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
@@ -350,6 +369,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -364,8 +384,8 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
>
|
>
|
||||||
</span>`
|
</span>`
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
</ha-dropdown>
|
</ha-md-button-menu>
|
||||||
${!this.optionsInSidebar
|
${!this.optionsInSidebar
|
||||||
? html`${this._warnings
|
? html`${this._warnings
|
||||||
? html`<ha-automation-editor-warning
|
? html`<ha-automation-editor-warning
|
||||||
@@ -817,47 +837,6 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
this._automationRowElement?.focus();
|
this._automationRowElement?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "test":
|
|
||||||
this._testCondition();
|
|
||||||
break;
|
|
||||||
case "rename":
|
|
||||||
this._renameCondition();
|
|
||||||
break;
|
|
||||||
case "duplicate":
|
|
||||||
this._duplicateCondition();
|
|
||||||
break;
|
|
||||||
case "copy":
|
|
||||||
this._copyCondition();
|
|
||||||
break;
|
|
||||||
case "cut":
|
|
||||||
this._cutCondition();
|
|
||||||
break;
|
|
||||||
case "move_up":
|
|
||||||
this._moveUp();
|
|
||||||
break;
|
|
||||||
case "move_down":
|
|
||||||
this._moveDown();
|
|
||||||
break;
|
|
||||||
case "toggle_yaml_mode":
|
|
||||||
this._toggleYamlMode(ev.target as HTMLElement);
|
|
||||||
break;
|
|
||||||
case "disable":
|
|
||||||
this._onDisable();
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
this._onDelete();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
rowStyles,
|
rowStyles,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import {
|
|||||||
CONDITION_BUILDING_BLOCKS,
|
CONDITION_BUILDING_BLOCKS,
|
||||||
subscribeConditions,
|
subscribeConditions,
|
||||||
} from "../../../../data/condition";
|
} from "../../../../data/condition";
|
||||||
import { subscribeLabFeature } from "../../../../data/labs";
|
import { subscribeLabFeatures } from "../../../../data/labs";
|
||||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import {
|
import {
|
||||||
@@ -90,14 +90,14 @@ export default class HaAutomationCondition extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
protected hassSubscribe() {
|
protected hassSubscribe() {
|
||||||
return [
|
return [
|
||||||
subscribeLabFeature(
|
subscribeLabFeatures(this.hass!.connection, (features) => {
|
||||||
this.hass!.connection,
|
this._newTriggersAndConditions =
|
||||||
"automation",
|
features.find(
|
||||||
"new_triggers_conditions",
|
(feature) =>
|
||||||
(feature) => {
|
feature.domain === "automation" &&
|
||||||
this._newTriggersAndConditions = feature.enabled;
|
feature.preview_feature === "new_triggers_conditions"
|
||||||
}
|
)?.enabled ?? false;
|
||||||
),
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
|
||||||
import { consume } from "@lit/context";
|
import { consume } from "@lit/context";
|
||||||
import {
|
import {
|
||||||
mdiAppleKeyboardCommand,
|
mdiAppleKeyboardCommand,
|
||||||
@@ -26,20 +25,18 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
|||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { UndoRedoController } from "../../../common/controllers/undo-redo-controller";
|
|
||||||
import { transform } from "../../../common/decorators/transform";
|
import { transform } from "../../../common/decorators/transform";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { goBack, navigate } from "../../../common/navigate";
|
import { goBack, navigate } from "../../../common/navigate";
|
||||||
import { promiseTimeout } from "../../../common/util/promise-timeout";
|
import { promiseTimeout } from "../../../common/util/promise-timeout";
|
||||||
import { afterNextRender } from "../../../common/util/render-status";
|
import { afterNextRender } from "../../../common/util/render-status";
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
import "../../../components/ha-dropdown";
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
|
|
||||||
import "../../../components/ha-fab";
|
import "../../../components/ha-fab";
|
||||||
import "../../../components/ha-fade-in";
|
import "../../../components/ha-fade-in";
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
|
import "../../../components/ha-list-item";
|
||||||
import "../../../components/ha-spinner";
|
import "../../../components/ha-spinner";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import "../../../components/ha-yaml-editor";
|
import "../../../components/ha-yaml-editor";
|
||||||
@@ -77,6 +74,7 @@ import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info
|
|||||||
import "../../../layouts/hass-subpage";
|
import "../../../layouts/hass-subpage";
|
||||||
import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin";
|
import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin";
|
||||||
import { PreventUnsavedMixin } from "../../../mixins/prevent-unsaved-mixin";
|
import { PreventUnsavedMixin } from "../../../mixins/prevent-unsaved-mixin";
|
||||||
|
import { UndoRedoController } from "../../../common/controllers/undo-redo-controller";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import type { Entries, HomeAssistant, Route } from "../../../types";
|
import type { Entries, HomeAssistant, Route } from "../../../types";
|
||||||
import { isMac } from "../../../util/is_mac";
|
import { isMac } from "../../../util/is_mac";
|
||||||
@@ -88,10 +86,10 @@ import {
|
|||||||
type EntityRegistryUpdate,
|
type EntityRegistryUpdate,
|
||||||
showAutomationSaveDialog,
|
showAutomationSaveDialog,
|
||||||
} from "./automation-save-dialog/show-dialog-automation-save";
|
} from "./automation-save-dialog/show-dialog-automation-save";
|
||||||
import { showAutomationSaveTimeoutDialog } from "./automation-save-timeout-dialog/show-dialog-automation-save-timeout";
|
|
||||||
import "./blueprint-automation-editor";
|
import "./blueprint-automation-editor";
|
||||||
import "./manual-automation-editor";
|
import "./manual-automation-editor";
|
||||||
import type { HaManualAutomationEditor } from "./manual-automation-editor";
|
import type { HaManualAutomationEditor } from "./manual-automation-editor";
|
||||||
|
import { showAutomationSaveTimeoutDialog } from "./automation-save-timeout-dialog/show-dialog-automation-save-timeout";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
@@ -294,10 +292,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
</ha-button>
|
</ha-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-dropdown
|
<ha-button-menu slot="toolbar-icon">
|
||||||
slot="toolbar-icon"
|
|
||||||
@wa-select=${this._handleDropdownSelect}
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
@@ -305,73 +300,99 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
|
||||||
${this._mode === "gui" && this.narrow
|
${this._mode === "gui" && this.narrow
|
||||||
? html`<ha-dropdown-item
|
? html`<ha-list-item
|
||||||
value="undo"
|
graphic="icon"
|
||||||
|
@click=${this._undo}
|
||||||
.disabled=${!this._undoRedoController.canUndo}
|
.disabled=${!this._undoRedoController.canUndo}
|
||||||
>
|
>
|
||||||
${this.hass.localize("ui.common.undo")}
|
${this.hass.localize("ui.common.undo")}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiUndo}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiUndo}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
value="redo"
|
graphic="icon"
|
||||||
|
@click=${this._redo}
|
||||||
.disabled=${!this._undoRedoController.canRedo}
|
.disabled=${!this._undoRedoController.canRedo}
|
||||||
>
|
>
|
||||||
${this.hass.localize("ui.common.redo")}
|
${this.hass.localize("ui.common.redo")}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiRedo}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiRedo}></ha-svg-icon>
|
||||||
</ha-dropdown-item>`
|
</ha-list-item>`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-dropdown-item .disabled=${!stateObj} value="info">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${!stateObj}
|
||||||
|
@click=${this._showInfo}
|
||||||
|
>
|
||||||
${this.hass.localize("ui.panel.config.automation.editor.show_info")}
|
${this.hass.localize("ui.panel.config.automation.editor.show_info")}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiInformationOutline}
|
.path=${mdiInformationOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
<ha-dropdown-item .disabled=${!stateObj} value="settings">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${!stateObj}
|
||||||
|
@click=${this._showSettings}
|
||||||
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.show_settings"
|
"ui.panel.config.automation.picker.show_settings"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiCog}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiCog}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
<ha-dropdown-item .disabled=${!stateObj} value="category">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${!stateObj}
|
||||||
|
@click=${this._editCategory}
|
||||||
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
`ui.panel.config.scene.picker.${this._registryEntry?.categories?.automation ? "edit_category" : "assign_category"}`
|
`ui.panel.config.scene.picker.${this._registryEntry?.categories?.automation ? "edit_category" : "assign_category"}`
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiTag}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiTag}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
<ha-dropdown-item .disabled=${!stateObj} value="run">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${!stateObj}
|
||||||
|
@click=${this._runActions}
|
||||||
|
>
|
||||||
${this.hass.localize("ui.panel.config.automation.editor.run")}
|
${this.hass.localize("ui.panel.config.automation.editor.run")}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlay}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiPlay}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
${stateObj && this.narrow
|
${stateObj && this.narrow
|
||||||
? html`<ha-dropdown-item value="trace">
|
? html`<a
|
||||||
|
href="/config/automation/trace/${encodeURIComponent(
|
||||||
|
this._config.id!
|
||||||
|
)}"
|
||||||
|
>
|
||||||
|
<ha-list-item graphic="icon">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.show_trace"
|
"ui.panel.config.automation.editor.show_trace"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiTransitConnection}
|
.path=${mdiTransitConnection}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-dropdown-item>`
|
</ha-list-item>
|
||||||
|
</a>`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
value="rename"
|
graphic="icon"
|
||||||
|
@click=${this._promptAutomationAlias}
|
||||||
.disabled=${this._readOnly ||
|
.disabled=${this._readOnly ||
|
||||||
!this.automationId ||
|
!this.automationId ||
|
||||||
this._mode === "yaml"}
|
this._mode === "yaml"}
|
||||||
>
|
>
|
||||||
${this.hass.localize("ui.panel.config.automation.editor.rename")}
|
${this.hass.localize("ui.panel.config.automation.editor.rename")}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiRenameBox}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
${!useBlueprint
|
${!useBlueprint
|
||||||
? html`
|
? html`
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
@click=${this._promptAutomationMode}
|
@click=${this._promptAutomationMode}
|
||||||
.disabled=${this._readOnly || this._mode === "yaml"}
|
.disabled=${this._readOnly || this._mode === "yaml"}
|
||||||
>
|
>
|
||||||
@@ -379,17 +400,18 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
"ui.panel.config.automation.editor.change_mode"
|
"ui.panel.config.automation.editor.change_mode"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiDebugStepOver}
|
.path=${mdiDebugStepOver}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
.disabled=${!!this._blueprintConfig ||
|
.disabled=${this._blueprintConfig ||
|
||||||
(!this._readOnly && !this.automationId)}
|
(!this._readOnly && !this.automationId)}
|
||||||
value="duplicate"
|
graphic="icon"
|
||||||
|
@click=${this._duplicate}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
this._readOnly
|
this._readOnly
|
||||||
@@ -397,60 +419,74 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
: "ui.panel.config.automation.editor.duplicate"
|
: "ui.panel.config.automation.editor.duplicate"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiPlusCircleMultipleOutline}
|
.path=${mdiPlusCircleMultipleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
${useBlueprint
|
${useBlueprint
|
||||||
? html`
|
? html`
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
value="take_control"
|
graphic="icon"
|
||||||
|
@click=${this._takeControl}
|
||||||
.disabled=${this._readOnly}
|
.disabled=${this._readOnly}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.take_control"
|
"ui.panel.config.automation.editor.take_control"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiFileEdit}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
</ha-dropdown-item>
|
slot="graphic"
|
||||||
|
.path=${mdiFileEdit}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-list-item>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-dropdown-item value="toggle_yaml_mode">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
@click=${this._mode === "gui"
|
||||||
|
? this._switchYamlMode
|
||||||
|
: this._switchUiMode}
|
||||||
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
`ui.panel.config.automation.editor.edit_${this._mode === "gui" ? "yaml" : "ui"}`
|
`ui.panel.config.automation.editor.edit_${this._mode === "gui" ? "yaml" : "ui"}`
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
<wa-divider></wa-divider>
|
<li divider role="separator"></li>
|
||||||
|
|
||||||
<ha-dropdown-item .disabled=${!stateObj} value="disable">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${!stateObj}
|
||||||
|
@click=${this._toggle}
|
||||||
|
>
|
||||||
${stateObj?.state === "off"
|
${stateObj?.state === "off"
|
||||||
? this.hass.localize("ui.panel.config.automation.editor.enable")
|
? this.hass.localize("ui.panel.config.automation.editor.enable")
|
||||||
: this.hass.localize("ui.panel.config.automation.editor.disable")}
|
: this.hass.localize("ui.panel.config.automation.editor.disable")}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${stateObj?.state === "off"
|
.path=${stateObj?.state === "off"
|
||||||
? mdiPlayCircleOutline
|
? mdiPlayCircleOutline
|
||||||
: mdiStopCircleOutline}
|
: mdiStopCircleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
.disabled=${!this.automationId}
|
.disabled=${!this.automationId}
|
||||||
.variant=${this.automationId ? "danger" : "default"}
|
class=${classMap({ warning: Boolean(this.automationId) })}
|
||||||
value="delete"
|
graphic="icon"
|
||||||
|
@click=${this._deleteConfirm}
|
||||||
>
|
>
|
||||||
${this.hass.localize("ui.panel.config.automation.picker.delete")}
|
${this.hass.localize("ui.panel.config.automation.picker.delete")}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class=${classMap({ warning: Boolean(this.automationId) })}
|
class=${classMap({ warning: Boolean(this.automationId) })}
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiDelete}
|
.path=${mdiDelete}
|
||||||
>
|
>
|
||||||
</ha-svg-icon>
|
</ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
</ha-dropdown>
|
</ha-button-menu>
|
||||||
<div
|
<div
|
||||||
class=${this._mode === "yaml" ? "yaml-mode" : ""}
|
class=${this._mode === "yaml" ? "yaml-mode" : ""}
|
||||||
@subscribe-automation-config=${this._subscribeAutomationConfig}
|
@subscribe-automation-config=${this._subscribeAutomationConfig}
|
||||||
@@ -1213,63 +1249,6 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
this._undoRedoController.redo();
|
this._undoRedoController.redo();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "undo":
|
|
||||||
this._undo();
|
|
||||||
break;
|
|
||||||
case "redo":
|
|
||||||
this._redo();
|
|
||||||
break;
|
|
||||||
case "info":
|
|
||||||
this._showInfo();
|
|
||||||
break;
|
|
||||||
case "settings":
|
|
||||||
this._showSettings();
|
|
||||||
break;
|
|
||||||
case "category":
|
|
||||||
this._editCategory();
|
|
||||||
break;
|
|
||||||
case "run":
|
|
||||||
this._runActions();
|
|
||||||
break;
|
|
||||||
case "rename":
|
|
||||||
this._promptAutomationAlias();
|
|
||||||
break;
|
|
||||||
case "change_mode":
|
|
||||||
this._promptAutomationMode();
|
|
||||||
break;
|
|
||||||
case "duplicate":
|
|
||||||
this._duplicate();
|
|
||||||
break;
|
|
||||||
case "take_control":
|
|
||||||
this._takeControl();
|
|
||||||
break;
|
|
||||||
case "toggle_yaml_mode":
|
|
||||||
if (this._mode === "gui") {
|
|
||||||
this._switchYamlMode();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this._switchUiMode();
|
|
||||||
break;
|
|
||||||
case "disable":
|
|
||||||
this._toggle();
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
this._deleteConfirm();
|
|
||||||
break;
|
|
||||||
case "trace":
|
|
||||||
this._showTrace();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
@@ -1322,6 +1301,13 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
margin-inline-end: 8px;
|
margin-inline-end: 8px;
|
||||||
margin-inline-start: initial;
|
margin-inline-start: initial;
|
||||||
}
|
}
|
||||||
|
li[role="separator"] {
|
||||||
|
border-bottom-color: var(--divider-color);
|
||||||
|
}
|
||||||
|
ha-button-menu a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
h1 {
|
h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
|
||||||
import {
|
import {
|
||||||
mdiDotsVertical,
|
mdiDotsVertical,
|
||||||
mdiDownload,
|
mdiDownload,
|
||||||
@@ -16,13 +15,11 @@ import { repeat } from "lit/directives/repeat";
|
|||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
import { formatDateTimeWithSeconds } from "../../../common/datetime/format_date_time";
|
import { formatDateTimeWithSeconds } from "../../../common/datetime/format_date_time";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { navigate } from "../../../common/navigate";
|
|
||||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
import "../../../components/ha-dropdown";
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
|
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
|
import "../../../components/ha-list-item";
|
||||||
import "../../../components/trace/ha-trace-blueprint-config";
|
import "../../../components/trace/ha-trace-blueprint-config";
|
||||||
import "../../../components/trace/ha-trace-config";
|
import "../../../components/trace/ha-trace-config";
|
||||||
import "../../../components/trace/ha-trace-logbook";
|
import "../../../components/trace/ha-trace-logbook";
|
||||||
@@ -107,7 +104,9 @@ export class HaAutomationTrace extends LitElement {
|
|||||||
appearance="plain"
|
appearance="plain"
|
||||||
size="small"
|
size="small"
|
||||||
class="trace-link"
|
class="trace-link"
|
||||||
@click=${this._navigateToAutomation}
|
href="/config/automation/edit/${encodeURIComponent(
|
||||||
|
stateObj.attributes.id
|
||||||
|
)}"
|
||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
@@ -115,50 +114,65 @@ export class HaAutomationTrace extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</ha-button>
|
</ha-button>
|
||||||
`
|
`
|
||||||
: nothing}
|
: ""}
|
||||||
<ha-dropdown
|
<ha-button-menu slot="toolbar-icon">
|
||||||
slot="toolbar-icon"
|
|
||||||
@wa-select=${this._handleDropdownSelect}
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
|
||||||
<ha-dropdown-item .disabled=${!stateObj} value="show_info">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${!stateObj}
|
||||||
|
@click=${this._showInfo}
|
||||||
|
>
|
||||||
${this.hass.localize("ui.panel.config.automation.editor.show_info")}
|
${this.hass.localize("ui.panel.config.automation.editor.show_info")}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiInformationOutline}
|
.path=${mdiInformationOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
${stateObj?.attributes.id && this.narrow
|
${stateObj?.attributes.id && this.narrow
|
||||||
? html`
|
? html`
|
||||||
<ha-dropdown-item value="edit_automation">
|
<a
|
||||||
|
class="trace-link"
|
||||||
|
href="/config/automation/edit/${encodeURIComponent(
|
||||||
|
stateObj.attributes.id
|
||||||
|
)}"
|
||||||
|
>
|
||||||
|
<ha-list-item graphic="icon">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.trace.edit_automation"
|
"ui.panel.config.automation.trace.edit_automation"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPencil}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
</ha-dropdown-item>
|
slot="graphic"
|
||||||
|
.path=${mdiPencil}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-list-item>
|
||||||
|
</a>
|
||||||
`
|
`
|
||||||
: nothing}
|
: ""}
|
||||||
|
|
||||||
<wa-divider></wa-divider>
|
<li divider role="separator"></li>
|
||||||
|
|
||||||
<ha-dropdown-item value="refresh">
|
<ha-list-item graphic="icon" @click=${this._refreshTraces}>
|
||||||
${this.hass.localize("ui.panel.config.automation.trace.refresh")}
|
${this.hass.localize("ui.panel.config.automation.trace.refresh")}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiRefresh}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiRefresh}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
<ha-dropdown-item .disabled=${!this._trace} value="download_trace">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${!this._trace}
|
||||||
|
@click=${this._downloadTrace}
|
||||||
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.trace.download_trace"
|
"ui.panel.config.automation.trace.download_trace"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiDownload}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiDownload}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
</ha-dropdown>
|
</ha-button-menu>
|
||||||
|
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
${this._traces && this._traces.length > 0
|
${this._traces && this._traces.length > 0
|
||||||
@@ -506,37 +520,6 @@ export class HaAutomationTrace extends LitElement {
|
|||||||
fireEvent(this, "hass-more-info", { entityId: this._entityId });
|
fireEvent(this, "hass-more-info", { entityId: this._entityId });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _navigateToAutomation() {
|
|
||||||
if (this._entityId && this.hass.states[this._entityId]) {
|
|
||||||
navigate(
|
|
||||||
`/config/automation/edit/${encodeURIComponent(this.hass.states[this._entityId].attributes.id)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "show_info":
|
|
||||||
this._showInfo();
|
|
||||||
break;
|
|
||||||
case "refresh":
|
|
||||||
this._refreshTraces();
|
|
||||||
break;
|
|
||||||
case "download_trace":
|
|
||||||
this._downloadTrace();
|
|
||||||
break;
|
|
||||||
case "edit_automation":
|
|
||||||
this._navigateToAutomation();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
|
|||||||
@@ -20,11 +20,10 @@ import { capitalizeFirstLetter } from "../../../../common/string/capitalize-firs
|
|||||||
import "../../../../components/ha-automation-row";
|
import "../../../../components/ha-automation-row";
|
||||||
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-dropdown";
|
|
||||||
import "../../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../../components/ha-dropdown-item";
|
|
||||||
import "../../../../components/ha-expansion-panel";
|
import "../../../../components/ha-expansion-panel";
|
||||||
import "../../../../components/ha-icon-button";
|
import "../../../../components/ha-icon-button";
|
||||||
|
import "../../../../components/ha-md-button-menu";
|
||||||
|
import "../../../../components/ha-md-menu-item";
|
||||||
import "../../../../components/ha-svg-icon";
|
import "../../../../components/ha-svg-icon";
|
||||||
import type {
|
import type {
|
||||||
Condition,
|
Condition,
|
||||||
@@ -37,7 +36,6 @@ import type { Action, Option } from "../../../../data/script";
|
|||||||
import { showPromptDialog } from "../../../../dialogs/generic/show-dialog-box";
|
import { showPromptDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import { isMac } from "../../../../util/is_mac";
|
import { isMac } from "../../../../util/is_mac";
|
||||||
import { showToast } from "../../../../util/toast";
|
|
||||||
import "../action/ha-automation-action";
|
import "../action/ha-automation-action";
|
||||||
import type HaAutomationAction from "../action/ha-automation-action";
|
import type HaAutomationAction from "../action/ha-automation-action";
|
||||||
import "../condition/ha-automation-condition";
|
import "../condition/ha-automation-condition";
|
||||||
@@ -48,6 +46,7 @@ import {
|
|||||||
overflowStyles,
|
overflowStyles,
|
||||||
rowStyles,
|
rowStyles,
|
||||||
} from "../styles";
|
} from "../styles";
|
||||||
|
import { showToast } from "../../../../util/toast";
|
||||||
|
|
||||||
@customElement("ha-automation-option-row")
|
@customElement("ha-automation-option-row")
|
||||||
export default class HaAutomationOptionRow extends LitElement {
|
export default class HaAutomationOptionRow extends LitElement {
|
||||||
@@ -156,12 +155,15 @@ export default class HaAutomationOptionRow extends LitElement {
|
|||||||
|
|
||||||
${this.option
|
${this.option
|
||||||
? html`
|
? html`
|
||||||
<ha-dropdown
|
<ha-md-button-menu
|
||||||
|
quick
|
||||||
slot="icons"
|
slot="icons"
|
||||||
@click=${preventDefaultStopPropagation}
|
@click=${preventDefaultStopPropagation}
|
||||||
|
@closed=${stopPropagation}
|
||||||
@keydown=${stopPropagation}
|
@keydown=${stopPropagation}
|
||||||
@wa-select=${this._handleDropdownSelect}
|
positioning="fixed"
|
||||||
placement="bottom-end"
|
anchor-corner="end-end"
|
||||||
|
menu-corner="start-end"
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
@@ -169,18 +171,24 @@ export default class HaAutomationOptionRow extends LitElement {
|
|||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
|
||||||
<ha-dropdown-item value="rename" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
<ha-svg-icon slot="icon" .path=${mdiRenameBox}></ha-svg-icon>
|
@click=${this._renameOption}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.rename"
|
"ui.panel.config.automation.editor.triggers.rename"
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<ha-dropdown-item value="duplicate" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
|
@click=${this._duplicateOption}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${mdiPlusCircleMultipleOutline}
|
.path=${mdiPlusCircleMultipleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
|
|
||||||
@@ -189,42 +197,45 @@ export default class HaAutomationOptionRow extends LitElement {
|
|||||||
"ui.panel.config.automation.editor.actions.duplicate"
|
"ui.panel.config.automation.editor.actions.duplicate"
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
${!this.optionsInSidebar
|
${!this.optionsInSidebar
|
||||||
? html`
|
? html`
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="move_up"
|
.clickAction=${this._moveUp}
|
||||||
.disabled=${this.disabled || !!this.first}
|
.disabled=${this.disabled || !!this.first}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.move_up"
|
"ui.panel.config.automation.editor.move_up"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiArrowUp}></ha-svg-icon
|
<ha-svg-icon
|
||||||
></ha-dropdown-item>
|
slot="start"
|
||||||
<ha-dropdown-item
|
.path=${mdiArrowUp}
|
||||||
value="move_down"
|
></ha-svg-icon
|
||||||
|
></ha-md-menu-item>
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._moveDown}
|
||||||
.disabled=${this.disabled || !!this.last}
|
.disabled=${this.disabled || !!this.last}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.move_down"
|
"ui.panel.config.automation.editor.move_down"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${mdiArrowDown}
|
.path=${mdiArrowDown}
|
||||||
></ha-svg-icon
|
></ha-svg-icon
|
||||||
></ha-dropdown-item>
|
></ha-md-menu-item>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="delete"
|
@click=${this._removeOption}
|
||||||
variant="danger"
|
class="warning"
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class="warning"
|
class="warning"
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${mdiDelete}
|
.path=${mdiDelete}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
@@ -235,6 +246,7 @@ export default class HaAutomationOptionRow extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -249,8 +261,8 @@ export default class HaAutomationOptionRow extends LitElement {
|
|||||||
>
|
>
|
||||||
</span>`
|
</span>`
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
</ha-dropdown>
|
</ha-md-button-menu>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
${!this.optionsInSidebar ? this._renderContent() : nothing}
|
${!this.optionsInSidebar ? this._renderContent() : nothing}
|
||||||
@@ -349,32 +361,6 @@ export default class HaAutomationOptionRow extends LitElement {
|
|||||||
fireEvent(this, "move-down");
|
fireEvent(this, "move-down");
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "rename":
|
|
||||||
this._renameOption();
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
this._removeOption();
|
|
||||||
break;
|
|
||||||
case "duplicate":
|
|
||||||
this._duplicateOption();
|
|
||||||
break;
|
|
||||||
case "move_up":
|
|
||||||
this._moveUp();
|
|
||||||
break;
|
|
||||||
case "move_down":
|
|
||||||
this._moveDown();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _removeOption = () => {
|
private _removeOption = () => {
|
||||||
if (this.option) {
|
if (this.option) {
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
@@ -527,6 +513,9 @@ export default class HaAutomationOptionRow extends LitElement {
|
|||||||
overflowStyles,
|
overflowStyles,
|
||||||
indentStyle,
|
indentStyle,
|
||||||
css`
|
css`
|
||||||
|
li[role="separator"] {
|
||||||
|
border-bottom-color: var(--divider-color);
|
||||||
|
}
|
||||||
h4 {
|
h4 {
|
||||||
color: var(--ha-color-text-secondary);
|
color: var(--ha-color-text-secondary);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
|
||||||
import {
|
import {
|
||||||
mdiAppleKeyboardCommand,
|
mdiAppleKeyboardCommand,
|
||||||
mdiContentCopy,
|
mdiContentCopy,
|
||||||
@@ -17,8 +16,8 @@ import { keyed } from "lit/directives/keyed";
|
|||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||||
import type { LocalizeKeys } from "../../../../common/translations/localize";
|
import type { LocalizeKeys } from "../../../../common/translations/localize";
|
||||||
import "../../../../components/ha-dropdown-item";
|
import "../../../../components/ha-md-divider";
|
||||||
import type { HaDropdownItem } from "../../../../components/ha-dropdown-item";
|
import "../../../../components/ha-md-menu-item";
|
||||||
import { ACTION_BUILDING_BLOCKS } from "../../../../data/action";
|
import { ACTION_BUILDING_BLOCKS } from "../../../../data/action";
|
||||||
import type { ActionSidebarConfig } from "../../../../data/automation";
|
import type { ActionSidebarConfig } from "../../../../data/automation";
|
||||||
import { domainToName } from "../../../../data/integration";
|
import { domainToName } from "../../../../data/integration";
|
||||||
@@ -98,9 +97,9 @@ export default class HaAutomationSidebarAction extends LitElement {
|
|||||||
title = `${domainToName(this.hass.localize, domain)}: ${
|
title = `${domainToName(this.hass.localize, domain)}: ${
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
`component.${domain}.services.${service}.name`,
|
`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
|
title
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
@@ -117,7 +116,6 @@ export default class HaAutomationSidebarAction extends LitElement {
|
|||||||
.yamlMode=${this.yamlMode}
|
.yamlMode=${this.yamlMode}
|
||||||
.warnings=${this._warnings}
|
.warnings=${this._warnings}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
@wa-select=${this._handleDropdownSelect}
|
|
||||||
>
|
>
|
||||||
<span slot="title">${title}</span>
|
<span slot="title">${title}</span>
|
||||||
<span slot="subtitle"
|
<span slot="subtitle"
|
||||||
@@ -128,35 +126,38 @@ export default class HaAutomationSidebarAction extends LitElement {
|
|||||||
: ""}</span
|
: ""}</span
|
||||||
>
|
>
|
||||||
|
|
||||||
<ha-dropdown-item slot="menu-items" value="run">
|
<ha-md-menu-item slot="menu-items" .clickAction=${this.config.run}>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlay}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiPlay}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize("ui.panel.config.automation.editor.actions.run")}
|
${this.hass.localize("ui.panel.config.automation.editor.actions.run")}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="rename"
|
.clickAction=${this.config.rename}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiRenameBox}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.rename"
|
"ui.panel.config.automation.editor.triggers.rename"
|
||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
<ha-md-divider
|
||||||
<wa-divider slot="menu-items"></wa-divider>
|
|
||||||
<ha-dropdown-item
|
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="duplicate"
|
role="separator"
|
||||||
|
tabindex="-1"
|
||||||
|
></ha-md-divider>
|
||||||
|
<ha-md-menu-item
|
||||||
|
slot="menu-items"
|
||||||
|
.clickAction=${this.config.duplicate}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${mdiPlusCircleMultipleOutline}
|
.path=${mdiPlusCircleMultipleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
@@ -165,9 +166,9 @@ export default class HaAutomationSidebarAction extends LitElement {
|
|||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item slot="menu-items" value="copy">
|
<ha-md-menu-item slot="menu-items" .clickAction=${this.config.copy}>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCopy}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.copy"
|
"ui.panel.config.automation.editor.triggers.copy"
|
||||||
@@ -177,6 +178,7 @@ export default class HaAutomationSidebarAction extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -188,13 +190,13 @@ export default class HaAutomationSidebarAction extends LitElement {
|
|||||||
</span>`
|
</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="cut"
|
.clickAction=${this.config.cut}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCut}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.cut"
|
"ui.panel.config.automation.editor.triggers.cut"
|
||||||
@@ -204,6 +206,7 @@ export default class HaAutomationSidebarAction extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -215,29 +218,32 @@ export default class HaAutomationSidebarAction extends LitElement {
|
|||||||
</span>`
|
</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="toggle_yaml_mode"
|
.clickAction=${this._toggleYamlMode}
|
||||||
.disabled=${!this.config.uiSupported || !!this._warnings}
|
.disabled=${!this.config.uiSupported || !!this._warnings}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
`ui.panel.config.automation.editor.edit_${!this.yamlMode ? "yaml" : "ui"}`
|
`ui.panel.config.automation.editor.edit_${!this.yamlMode ? "yaml" : "ui"}`
|
||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
<ha-md-divider
|
||||||
<wa-divider slot="menu-items"></wa-divider>
|
|
||||||
<ha-dropdown-item
|
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="disable"
|
role="separator"
|
||||||
|
tabindex="-1"
|
||||||
|
></ha-md-divider>
|
||||||
|
<ha-md-menu-item
|
||||||
|
slot="menu-items"
|
||||||
|
.clickAction=${this.config.disable}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${rowDisabled ? mdiPlayCircleOutline : mdiStopCircleOutline}
|
.path=${rowDisabled ? mdiPlayCircleOutline : mdiStopCircleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
@@ -246,14 +252,14 @@ export default class HaAutomationSidebarAction extends LitElement {
|
|||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="delete"
|
.clickAction=${this.config.delete}
|
||||||
variant="danger"
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
|
class="warning"
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiDelete}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiDelete}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.delete"
|
"ui.panel.config.automation.editor.actions.delete"
|
||||||
@@ -263,6 +269,7 @@ export default class HaAutomationSidebarAction extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -278,7 +285,7 @@ export default class HaAutomationSidebarAction extends LitElement {
|
|||||||
</span>`
|
</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
${description && !this.yamlMode
|
${description && !this.yamlMode
|
||||||
? html`<div class="description">${description}</div>`
|
? html`<div class="description">${description}</div>`
|
||||||
: keyed(
|
: keyed(
|
||||||
@@ -334,41 +341,6 @@ export default class HaAutomationSidebarAction extends LitElement {
|
|||||||
fireEvent(this, "toggle-yaml-mode");
|
fireEvent(this, "toggle-yaml-mode");
|
||||||
};
|
};
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "rename":
|
|
||||||
this.config.rename();
|
|
||||||
break;
|
|
||||||
case "run":
|
|
||||||
this.config.run();
|
|
||||||
break;
|
|
||||||
case "duplicate":
|
|
||||||
this.config.duplicate();
|
|
||||||
break;
|
|
||||||
case "copy":
|
|
||||||
this.config.copy();
|
|
||||||
break;
|
|
||||||
case "cut":
|
|
||||||
this.config.cut();
|
|
||||||
break;
|
|
||||||
case "toggle_yaml_mode":
|
|
||||||
this._toggleYamlMode();
|
|
||||||
break;
|
|
||||||
case "disable":
|
|
||||||
this.config.disable();
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
this.config.delete();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = [sidebarEditorStyles, overflowStyles];
|
static styles = [sidebarEditorStyles, overflowStyles];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ import { css, html, LitElement, nothing } from "lit";
|
|||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { preventDefaultStopPropagation } from "../../../../common/dom/prevent_default_stop_propagation";
|
|
||||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-dialog-header";
|
import "../../../../components/ha-dialog-header";
|
||||||
import "../../../../components/ha-dropdown";
|
|
||||||
import "../../../../components/ha-icon-button";
|
import "../../../../components/ha-icon-button";
|
||||||
import { ScrollableFadeMixin } from "../../../../mixins/scrollable-fade-mixin";
|
import "../../../../components/ha-md-button-menu";
|
||||||
|
import "../../../../components/ha-md-divider";
|
||||||
import { haStyleScrollbar } from "../../../../resources/styles";
|
import { haStyleScrollbar } from "../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import "../ha-automation-editor-warning";
|
import "../ha-automation-editor-warning";
|
||||||
|
import { ScrollableFadeMixin } from "../../../../mixins/scrollable-fade-mixin";
|
||||||
|
|
||||||
export interface SidebarOverflowMenuEntry {
|
export interface SidebarOverflowMenuEntry {
|
||||||
clickAction: () => void;
|
clickAction: () => void;
|
||||||
@@ -36,10 +36,6 @@ export default class HaAutomationSidebarCard extends ScrollableFadeMixin(
|
|||||||
|
|
||||||
@property({ attribute: false }) public warnings?: string[];
|
@property({ attribute: false }) public warnings?: string[];
|
||||||
|
|
||||||
@property({ attribute: false }) public handleDropdownSelect!: (
|
|
||||||
ev: CustomEvent
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public narrow = false;
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
@query(".card-content") private _contentElement!: HTMLDivElement;
|
@query(".card-content") private _contentElement!: HTMLDivElement;
|
||||||
@@ -67,10 +63,14 @@ export default class HaAutomationSidebarCard extends ScrollableFadeMixin(
|
|||||||
<slot slot="title" name="title"></slot>
|
<slot slot="title" name="title"></slot>
|
||||||
<slot slot="subtitle" name="subtitle"></slot>
|
<slot slot="subtitle" name="subtitle"></slot>
|
||||||
<slot name="overflow-menu" slot="actionItems">
|
<slot name="overflow-menu" slot="actionItems">
|
||||||
<ha-dropdown
|
<ha-md-button-menu
|
||||||
@click=${preventDefaultStopPropagation}
|
quick
|
||||||
|
@click=${this._openOverflowMenu}
|
||||||
@keydown=${stopPropagation}
|
@keydown=${stopPropagation}
|
||||||
placement="bottom-end"
|
@closed=${stopPropagation}
|
||||||
|
.positioning=${this.narrow ? "absolute" : "fixed"}
|
||||||
|
anchor-corner="end-end"
|
||||||
|
menu-corner="start-end"
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
@@ -78,7 +78,7 @@ export default class HaAutomationSidebarCard extends ScrollableFadeMixin(
|
|||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<slot name="menu-items"></slot>
|
<slot name="menu-items"></slot>
|
||||||
</ha-dropdown>
|
</ha-md-button-menu>
|
||||||
</slot>
|
</slot>
|
||||||
</ha-dialog-header>
|
</ha-dialog-header>
|
||||||
${this.warnings
|
${this.warnings
|
||||||
@@ -100,6 +100,11 @@ export default class HaAutomationSidebarCard extends ScrollableFadeMixin(
|
|||||||
fireEvent(this, "close-sidebar");
|
fireEvent(this, "close-sidebar");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _openOverflowMenu(ev: MouseEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [
|
||||||
...super.styles,
|
...super.styles,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
|
||||||
import {
|
import {
|
||||||
mdiAppleKeyboardCommand,
|
mdiAppleKeyboardCommand,
|
||||||
mdiContentCopy,
|
mdiContentCopy,
|
||||||
@@ -17,11 +16,9 @@ import { classMap } from "lit/directives/class-map";
|
|||||||
import { keyed } from "lit/directives/keyed";
|
import { keyed } from "lit/directives/keyed";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||||
import "../../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../../components/ha-dropdown-item";
|
|
||||||
import type {
|
import type {
|
||||||
ConditionSidebarConfig,
|
|
||||||
LegacyCondition,
|
LegacyCondition,
|
||||||
|
ConditionSidebarConfig,
|
||||||
} from "../../../../data/automation";
|
} from "../../../../data/automation";
|
||||||
import { testCondition } from "../../../../data/automation";
|
import { testCondition } from "../../../../data/automation";
|
||||||
import {
|
import {
|
||||||
@@ -120,7 +117,6 @@ export default class HaAutomationSidebarCondition extends LitElement {
|
|||||||
.yamlMode=${this.yamlMode}
|
.yamlMode=${this.yamlMode}
|
||||||
.warnings=${this._warnings}
|
.warnings=${this._warnings}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
@wa-select=${this._handleDropdownSelect}
|
|
||||||
>
|
>
|
||||||
<span slot="title">${title}</span>
|
<span slot="title">${title}</span>
|
||||||
<span slot="subtitle"
|
<span slot="subtitle"
|
||||||
@@ -128,38 +124,42 @@ export default class HaAutomationSidebarCondition extends LitElement {
|
|||||||
? ` (${this.hass.localize("ui.panel.config.automation.editor.actions.disabled")})`
|
? ` (${this.hass.localize("ui.panel.config.automation.editor.actions.disabled")})`
|
||||||
: ""}</span
|
: ""}</span
|
||||||
>
|
>
|
||||||
<ha-dropdown-item slot="menu-items" value="test">
|
<ha-md-menu-item slot="menu-items" .clickAction=${this._testCondition}>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiFlask}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiFlask}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.conditions.test"
|
"ui.panel.config.automation.editor.conditions.test"
|
||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="rename"
|
.clickAction=${this.config.rename}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiRenameBox}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.rename"
|
"ui.panel.config.automation.editor.triggers.rename"
|
||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<wa-divider slot="menu-items"></wa-divider>
|
<ha-md-divider
|
||||||
|
|
||||||
<ha-dropdown-item
|
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="duplicate"
|
role="separator"
|
||||||
|
tabindex="-1"
|
||||||
|
></ha-md-divider>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
slot="menu-items"
|
||||||
|
.clickAction=${this.config.duplicate}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${mdiPlusCircleMultipleOutline}
|
.path=${mdiPlusCircleMultipleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
@@ -168,10 +168,10 @@ export default class HaAutomationSidebarCondition extends LitElement {
|
|||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<ha-dropdown-item slot="menu-items" value="copy">
|
<ha-md-menu-item slot="menu-items" .clickAction=${this.config.copy}>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCopy}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.copy"
|
"ui.panel.config.automation.editor.triggers.copy"
|
||||||
@@ -181,6 +181,7 @@ export default class HaAutomationSidebarCondition extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -192,14 +193,14 @@ export default class HaAutomationSidebarCondition extends LitElement {
|
|||||||
</span>`
|
</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="cut"
|
.clickAction=${this.config.cut}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCut}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.cut"
|
"ui.panel.config.automation.editor.triggers.cut"
|
||||||
@@ -209,6 +210,7 @@ export default class HaAutomationSidebarCondition extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -220,29 +222,32 @@ export default class HaAutomationSidebarCondition extends LitElement {
|
|||||||
</span>`
|
</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="toggle_yaml_mode"
|
.clickAction=${this._toggleYamlMode}
|
||||||
.disabled=${!this.config.uiSupported || !!this._warnings}
|
.disabled=${!this.config.uiSupported || !!this._warnings}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
`ui.panel.config.automation.editor.edit_${!this.yamlMode ? "yaml" : "ui"}`
|
`ui.panel.config.automation.editor.edit_${!this.yamlMode ? "yaml" : "ui"}`
|
||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
<ha-md-divider
|
||||||
<wa-divider slot="menu-items"></wa-divider>
|
|
||||||
<ha-dropdown-item
|
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="disable"
|
role="separator"
|
||||||
|
tabindex="-1"
|
||||||
|
></ha-md-divider>
|
||||||
|
<ha-md-menu-item
|
||||||
|
slot="menu-items"
|
||||||
|
.clickAction=${this.config.disable}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${rowDisabled ? mdiPlayCircleOutline : mdiStopCircleOutline}
|
.path=${rowDisabled ? mdiPlayCircleOutline : mdiStopCircleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
@@ -251,14 +256,14 @@ export default class HaAutomationSidebarCondition extends LitElement {
|
|||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="delete"
|
.clickAction=${this.config.delete}
|
||||||
variant="danger"
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
|
class="warning"
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiDelete}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiDelete}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.delete"
|
"ui.panel.config.automation.editor.actions.delete"
|
||||||
@@ -268,6 +273,7 @@ export default class HaAutomationSidebarCondition extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -283,7 +289,7 @@ export default class HaAutomationSidebarCondition extends LitElement {
|
|||||||
</span>`
|
</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
${description && !this.yamlMode
|
${description && !this.yamlMode
|
||||||
? html`<div class="description">${description}</div>`
|
? html`<div class="description">${description}</div>`
|
||||||
: keyed(
|
: keyed(
|
||||||
@@ -413,41 +419,6 @@ export default class HaAutomationSidebarCondition extends LitElement {
|
|||||||
fireEvent(this, "toggle-yaml-mode");
|
fireEvent(this, "toggle-yaml-mode");
|
||||||
};
|
};
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "rename":
|
|
||||||
this.config.rename();
|
|
||||||
break;
|
|
||||||
case "test":
|
|
||||||
this._testCondition();
|
|
||||||
break;
|
|
||||||
case "duplicate":
|
|
||||||
this.config.duplicate();
|
|
||||||
break;
|
|
||||||
case "copy":
|
|
||||||
this.config.copy();
|
|
||||||
break;
|
|
||||||
case "cut":
|
|
||||||
this.config.cut();
|
|
||||||
break;
|
|
||||||
case "toggle_yaml_mode":
|
|
||||||
this._toggleYamlMode();
|
|
||||||
break;
|
|
||||||
case "disable":
|
|
||||||
this.config.disable();
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
this.config.delete();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = [
|
static styles = [
|
||||||
sidebarEditorStyles,
|
sidebarEditorStyles,
|
||||||
overflowStyles,
|
overflowStyles,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
|
||||||
import {
|
import {
|
||||||
mdiAppleKeyboardCommand,
|
mdiAppleKeyboardCommand,
|
||||||
mdiDelete,
|
mdiDelete,
|
||||||
@@ -7,9 +6,8 @@ import {
|
|||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
|
import "../../../../components/ha-md-divider";
|
||||||
import "../../../../components/ha-dropdown-item";
|
import "../../../../components/ha-md-menu-item";
|
||||||
import type { HaDropdownItem } from "../../../../components/ha-dropdown-item";
|
|
||||||
import "../../../../components/ha-svg-icon";
|
import "../../../../components/ha-svg-icon";
|
||||||
import type { OptionSidebarConfig } from "../../../../data/automation";
|
import type { OptionSidebarConfig } from "../../../../data/automation";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
@@ -52,34 +50,33 @@ export default class HaAutomationSidebarOption extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.isWide=${this.isWide}
|
.isWide=${this.isWide}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
@wa-select=${this._handleDropdownSelect}
|
|
||||||
>
|
>
|
||||||
<span slot="title">${title}</span>
|
<span slot="title">${title}</span>
|
||||||
<span slot="subtitle">${subtitle}</span>
|
<span slot="subtitle">${subtitle}</span>
|
||||||
${this.config.defaultOption
|
${this.config.defaultOption
|
||||||
? html`<span slot="overflow-menu"></span>`
|
? html`<span slot="overflow-menu"></span>`
|
||||||
: html`
|
: html`
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="rename"
|
.clickAction=${this.config.rename}
|
||||||
.disabled=${!!disabled}
|
.disabled=${!!disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiRenameBox}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.rename"
|
"ui.panel.config.automation.editor.triggers.rename"
|
||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="duplicate"
|
@click=${this.config.duplicate}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${mdiPlusCircleMultipleOutline}
|
.path=${mdiPlusCircleMultipleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
@@ -88,15 +85,19 @@ export default class HaAutomationSidebarOption extends LitElement {
|
|||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<wa-divider slot="menu-items"></wa-divider>
|
<ha-md-divider
|
||||||
<ha-dropdown-item
|
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="delete"
|
role="separator"
|
||||||
|
tabindex="-1"
|
||||||
|
></ha-md-divider>
|
||||||
|
<ha-md-menu-item
|
||||||
|
slot="menu-items"
|
||||||
|
.clickAction=${this.config.delete}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
variant="danger"
|
class="warning"
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiDelete}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiDelete}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.type.choose.remove_option"
|
"ui.panel.config.automation.editor.actions.type.choose.remove_option"
|
||||||
@@ -122,33 +123,13 @@ export default class HaAutomationSidebarOption extends LitElement {
|
|||||||
</span>`
|
</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
`}
|
`}
|
||||||
|
|
||||||
<div class="description">${description}</div>
|
<div class="description">${description}</div>
|
||||||
</ha-automation-sidebar-card>`;
|
</ha-automation-sidebar-card>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "rename":
|
|
||||||
this.config.rename();
|
|
||||||
break;
|
|
||||||
case "duplicate":
|
|
||||||
this.config.duplicate();
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
this.config.delete();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = [sidebarEditorStyles, overflowStyles];
|
static styles = [sidebarEditorStyles, overflowStyles];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import { customElement, property, query, state } from "lit/decorators";
|
|||||||
import { keyed } from "lit/directives/keyed";
|
import { keyed } from "lit/directives/keyed";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import type { LocalizeKeys } from "../../../../common/translations/localize";
|
import type { LocalizeKeys } from "../../../../common/translations/localize";
|
||||||
import "../../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../../components/ha-dropdown-item";
|
|
||||||
import type { ScriptFieldSidebarConfig } from "../../../../data/automation";
|
import type { ScriptFieldSidebarConfig } from "../../../../data/automation";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import { isMac } from "../../../../util/is_mac";
|
import { isMac } from "../../../../util/is_mac";
|
||||||
@@ -64,30 +62,29 @@ export default class HaAutomationSidebarScriptFieldSelector extends LitElement {
|
|||||||
.yamlMode=${this.yamlMode}
|
.yamlMode=${this.yamlMode}
|
||||||
.warnings=${this._warnings}
|
.warnings=${this._warnings}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
@wa-select=${this._handleDropdownSelect}
|
|
||||||
>
|
>
|
||||||
<span slot="title">${title}</span>
|
<span slot="title">${title}</span>
|
||||||
<span slot="subtitle">${subtitle}</span>
|
<span slot="subtitle">${subtitle}</span>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="toggle_yaml_mode"
|
.clickAction=${this._toggleYamlMode}
|
||||||
.disabled=${!!this._warnings}
|
.disabled=${!!this._warnings}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
`ui.panel.config.automation.editor.edit_${!this.yamlMode ? "yaml" : "ui"}`
|
`ui.panel.config.automation.editor.edit_${!this.yamlMode ? "yaml" : "ui"}`
|
||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="delete"
|
.clickAction=${this.config.delete}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
variant="danger"
|
class="warning"
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiDelete}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiDelete}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.delete"
|
"ui.panel.config.automation.editor.actions.delete"
|
||||||
@@ -97,6 +94,7 @@ export default class HaAutomationSidebarScriptFieldSelector extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -112,7 +110,7 @@ export default class HaAutomationSidebarScriptFieldSelector extends LitElement {
|
|||||||
</span>`
|
</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
${keyed(
|
${keyed(
|
||||||
this.sidebarKey,
|
this.sidebarKey,
|
||||||
html`<ha-script-field-selector-editor
|
html`<ha-script-field-selector-editor
|
||||||
@@ -162,23 +160,6 @@ export default class HaAutomationSidebarScriptFieldSelector extends LitElement {
|
|||||||
fireEvent(this, "toggle-yaml-mode");
|
fireEvent(this, "toggle-yaml-mode");
|
||||||
};
|
};
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "toggle_yaml_mode":
|
|
||||||
this._toggleYamlMode();
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
this.config.delete();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = sidebarEditorStyles;
|
static styles = sidebarEditorStyles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import { html, LitElement, nothing } from "lit";
|
|||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { keyed } from "lit/directives/keyed";
|
import { keyed } from "lit/directives/keyed";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import "../../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../../components/ha-dropdown-item";
|
|
||||||
import type { ScriptFieldSidebarConfig } from "../../../../data/automation";
|
import type { ScriptFieldSidebarConfig } from "../../../../data/automation";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import { isMac } from "../../../../util/is_mac";
|
import { isMac } from "../../../../util/is_mac";
|
||||||
@@ -58,29 +56,28 @@ export default class HaAutomationSidebarScriptField extends LitElement {
|
|||||||
.yamlMode=${this.yamlMode}
|
.yamlMode=${this.yamlMode}
|
||||||
.warnings=${this._warnings}
|
.warnings=${this._warnings}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
@wa-select=${this._handleDropdownSelect}
|
|
||||||
>
|
>
|
||||||
<span slot="title">${title}</span>
|
<span slot="title">${title}</span>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="toggle_yaml_mode"
|
.clickAction=${this._toggleYamlMode}
|
||||||
.disabled=${!!this._warnings}
|
.disabled=${!!this._warnings}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
`ui.panel.config.automation.editor.edit_${!this.yamlMode ? "yaml" : "ui"}`
|
`ui.panel.config.automation.editor.edit_${!this.yamlMode ? "yaml" : "ui"}`
|
||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="delete"
|
.clickAction=${this.config.delete}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
variant="danger"
|
class="warning"
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiDelete}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiDelete}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.delete"
|
"ui.panel.config.automation.editor.actions.delete"
|
||||||
@@ -90,6 +87,7 @@ export default class HaAutomationSidebarScriptField extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -105,7 +103,7 @@ export default class HaAutomationSidebarScriptField extends LitElement {
|
|||||||
</span>`
|
</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
${keyed(
|
${keyed(
|
||||||
this.sidebarKey,
|
this.sidebarKey,
|
||||||
html`<ha-script-field-editor
|
html`<ha-script-field-editor
|
||||||
@@ -156,23 +154,6 @@ export default class HaAutomationSidebarScriptField extends LitElement {
|
|||||||
fireEvent(this, "toggle-yaml-mode");
|
fireEvent(this, "toggle-yaml-mode");
|
||||||
};
|
};
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "toggle_yaml_mode":
|
|
||||||
this._toggleYamlMode();
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
this.config.delete();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = [sidebarEditorStyles, overflowStyles];
|
static styles = [sidebarEditorStyles, overflowStyles];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
|
||||||
import {
|
import {
|
||||||
mdiAppleKeyboardCommand,
|
mdiAppleKeyboardCommand,
|
||||||
mdiContentCopy,
|
mdiContentCopy,
|
||||||
@@ -16,8 +15,6 @@ import { customElement, property, query, state } from "lit/decorators";
|
|||||||
import { keyed } from "lit/directives/keyed";
|
import { keyed } from "lit/directives/keyed";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||||
import "../../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../../components/ha-dropdown-item";
|
|
||||||
import type {
|
import type {
|
||||||
LegacyTrigger,
|
LegacyTrigger,
|
||||||
TriggerSidebarConfig,
|
TriggerSidebarConfig,
|
||||||
@@ -102,7 +99,6 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
|||||||
.yamlMode=${this.yamlMode}
|
.yamlMode=${this.yamlMode}
|
||||||
.warnings=${this._warnings}
|
.warnings=${this._warnings}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
@wa-select=${this._handleDropdownSelect}
|
|
||||||
>
|
>
|
||||||
<span slot="title">${title}</span>
|
<span slot="title">${title}</span>
|
||||||
<span slot="subtitle"
|
<span slot="subtitle"
|
||||||
@@ -110,56 +106,60 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
|||||||
? ` (${this.hass.localize("ui.panel.config.automation.editor.actions.disabled")})`
|
? ` (${this.hass.localize("ui.panel.config.automation.editor.actions.disabled")})`
|
||||||
: ""}</span
|
: ""}</span
|
||||||
>
|
>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="rename"
|
.clickAction=${this.config.rename}
|
||||||
.disabled=${this.disabled || type === "list"}
|
.disabled=${this.disabled || type === "list"}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiRenameBox}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.rename"
|
"ui.panel.config.automation.editor.triggers.rename"
|
||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
${!this.yamlMode &&
|
${!this.yamlMode &&
|
||||||
!("id" in this.config.config) &&
|
!("id" in this.config.config) &&
|
||||||
!this._requestShowId
|
!this._requestShowId
|
||||||
? html`<ha-dropdown-item
|
? html`<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="show_id"
|
.clickAction=${this._showTriggerId}
|
||||||
.disabled=${this.disabled || type === "list"}
|
.disabled=${this.disabled || type === "list"}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiIdentifier}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiIdentifier}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.edit_id"
|
"ui.panel.config.automation.editor.triggers.edit_id"
|
||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>`
|
</ha-md-menu-item>`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<wa-divider slot="menu-items"></wa-divider>
|
<ha-md-divider
|
||||||
|
|
||||||
<ha-dropdown-item
|
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="duplicate"
|
role="separator"
|
||||||
|
tabindex="-1"
|
||||||
|
></ha-md-divider>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
slot="menu-items"
|
||||||
|
.clickAction=${this.config.duplicate}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.duplicate"
|
"ui.panel.config.automation.editor.triggers.duplicate"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${mdiPlusCircleMultipleOutline}
|
.path=${mdiPlusCircleMultipleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<ha-dropdown-item slot="menu-items" value="copy">
|
<ha-md-menu-item slot="menu-items" .clickAction=${this.config.copy}>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCopy}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.copy"
|
"ui.panel.config.automation.editor.triggers.copy"
|
||||||
@@ -169,6 +169,7 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -180,14 +181,14 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
|||||||
</span>`
|
</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="cut"
|
.clickAction=${this.config.cut}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCut}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.cut"
|
"ui.panel.config.automation.editor.triggers.cut"
|
||||||
@@ -197,6 +198,7 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -208,28 +210,32 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
|||||||
</span>`
|
</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="toggle_yaml_mode"
|
.clickAction=${this._toggleYamlMode}
|
||||||
.disabled=${!this.config.uiSupported || !!this._warnings}
|
.disabled=${!this.config.uiSupported || !!this._warnings}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
`ui.panel.config.automation.editor.edit_${!this.yamlMode ? "yaml" : "ui"}`
|
`ui.panel.config.automation.editor.edit_${!this.yamlMode ? "yaml" : "ui"}`
|
||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<wa-divider slot="menu-items"></wa-divider>
|
<ha-md-divider
|
||||||
<ha-dropdown-item
|
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="disable"
|
role="separator"
|
||||||
|
tabindex="-1"
|
||||||
|
></ha-md-divider>
|
||||||
|
<ha-md-menu-item
|
||||||
|
slot="menu-items"
|
||||||
|
.clickAction=${this.config.disable}
|
||||||
.disabled=${this.disabled || type === "list"}
|
.disabled=${this.disabled || type === "list"}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${rowDisabled ? mdiPlayCircleOutline : mdiStopCircleOutline}
|
.path=${rowDisabled ? mdiPlayCircleOutline : mdiStopCircleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
@@ -238,14 +244,14 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
|||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
slot="menu-items"
|
slot="menu-items"
|
||||||
value="delete"
|
.clickAction=${this.config.delete}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
variant="danger"
|
class="warning"
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiDelete}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiDelete}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.delete"
|
"ui.panel.config.automation.editor.actions.delete"
|
||||||
@@ -255,6 +261,7 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -270,7 +277,7 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
|||||||
</span>`
|
</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
${keyed(
|
${keyed(
|
||||||
this.sidebarKey,
|
this.sidebarKey,
|
||||||
html`<ha-automation-trigger-editor
|
html`<ha-automation-trigger-editor
|
||||||
@@ -328,41 +335,6 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
|||||||
this._requestShowId = true;
|
this._requestShowId = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "rename":
|
|
||||||
this.config.rename();
|
|
||||||
break;
|
|
||||||
case "show_id":
|
|
||||||
this._showTriggerId();
|
|
||||||
break;
|
|
||||||
case "duplicate":
|
|
||||||
this.config.duplicate();
|
|
||||||
break;
|
|
||||||
case "copy":
|
|
||||||
this.config.copy();
|
|
||||||
break;
|
|
||||||
case "cut":
|
|
||||||
this.config.cut();
|
|
||||||
break;
|
|
||||||
case "toggle_yaml_mode":
|
|
||||||
this._toggleYamlMode();
|
|
||||||
break;
|
|
||||||
case "disable":
|
|
||||||
this.config.disable();
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
this.config.delete();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = [sidebarEditorStyles, overflowStyles];
|
static styles = [sidebarEditorStyles, overflowStyles];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
|
||||||
import { consume } from "@lit/context";
|
import { consume } from "@lit/context";
|
||||||
import {
|
import {
|
||||||
mdiAppleKeyboardCommand,
|
mdiAppleKeyboardCommand,
|
||||||
@@ -35,11 +34,11 @@ import "../../../../components/ha-alert";
|
|||||||
import "../../../../components/ha-automation-row";
|
import "../../../../components/ha-automation-row";
|
||||||
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-dropdown";
|
|
||||||
import "../../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../../components/ha-dropdown-item";
|
|
||||||
import "../../../../components/ha-expansion-panel";
|
import "../../../../components/ha-expansion-panel";
|
||||||
import "../../../../components/ha-icon-button";
|
import "../../../../components/ha-icon-button";
|
||||||
|
import "../../../../components/ha-md-button-menu";
|
||||||
|
import "../../../../components/ha-md-divider";
|
||||||
|
import "../../../../components/ha-md-menu-item";
|
||||||
import "../../../../components/ha-svg-icon";
|
import "../../../../components/ha-svg-icon";
|
||||||
import { TRIGGER_ICONS } from "../../../../components/ha-trigger-icon";
|
import { TRIGGER_ICONS } from "../../../../components/ha-trigger-icon";
|
||||||
import type {
|
import type {
|
||||||
@@ -209,35 +208,41 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
|
|
||||||
<slot name="icons" slot="icons"></slot>
|
<slot name="icons" slot="icons"></slot>
|
||||||
|
|
||||||
<ha-dropdown
|
<ha-md-button-menu
|
||||||
|
quick
|
||||||
slot="icons"
|
slot="icons"
|
||||||
@click=${preventDefaultStopPropagation}
|
@click=${preventDefaultStopPropagation}
|
||||||
@keydown=${stopPropagation}
|
@keydown=${stopPropagation}
|
||||||
@wa-select=${this._handleDropdownSelect}
|
@closed=${stopPropagation}
|
||||||
placement="bottom-end"
|
positioning="fixed"
|
||||||
|
anchor-corner="end-end"
|
||||||
|
menu-corner="start-end"
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="rename"
|
.clickAction=${this._renameTrigger}
|
||||||
.disabled=${this.disabled || type === "list"}
|
.disabled=${this.disabled || type === "list"}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiRenameBox}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.rename"
|
"ui.panel.config.automation.editor.triggers.rename"
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<wa-divider></wa-divider>
|
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||||
|
|
||||||
<ha-dropdown-item value="duplicate" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._duplicateTrigger}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${mdiPlusCircleMultipleOutline}
|
.path=${mdiPlusCircleMultipleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
|
|
||||||
@@ -246,10 +251,13 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
"ui.panel.config.automation.editor.actions.duplicate"
|
"ui.panel.config.automation.editor.actions.duplicate"
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<ha-dropdown-item value="copy" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCopy}></ha-svg-icon>
|
.clickAction=${this._copyTrigger}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.copy"
|
"ui.panel.config.automation.editor.triggers.copy"
|
||||||
@@ -258,6 +266,7 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -268,10 +277,13 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
<span>C</span>
|
<span>C</span>
|
||||||
</span>`
|
</span>`
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<ha-dropdown-item value="cut" .disabled=${this.disabled}>
|
<ha-md-menu-item
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCut}></ha-svg-icon>
|
.clickAction=${this._cutTrigger}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.cut"
|
"ui.panel.config.automation.editor.triggers.cut"
|
||||||
@@ -280,6 +292,7 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -290,51 +303,51 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
<span>X</span>
|
<span>X</span>
|
||||||
</span>`
|
</span>`
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
${!this.optionsInSidebar
|
${!this.optionsInSidebar
|
||||||
? html`
|
? html`
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="move_up"
|
.clickAction=${this._moveUp}
|
||||||
.disabled=${this.disabled || !!this.first}
|
.disabled=${this.disabled || !!this.first}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.move_up"
|
"ui.panel.config.automation.editor.move_up"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiArrowUp}></ha-svg-icon
|
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
||||||
></ha-dropdown-item>
|
></ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="move_down"
|
.clickAction=${this._moveDown}
|
||||||
.disabled=${this.disabled || !!this.last}
|
.disabled=${this.disabled || !!this.last}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.move_down"
|
"ui.panel.config.automation.editor.move_down"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiArrowDown}></ha-svg-icon
|
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
||||||
></ha-dropdown-item>
|
></ha-md-menu-item>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="toggle_yaml_mode"
|
.clickAction=${this._toggleYamlMode}
|
||||||
.disabled=${!supported || !!this._warnings}
|
.disabled=${!supported || !!this._warnings}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
`ui.panel.config.automation.editor.edit_${!yamlMode ? "yaml" : "ui"}`
|
`ui.panel.config.automation.editor.edit_${!yamlMode ? "yaml" : "ui"}`
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<wa-divider></wa-divider>
|
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||||
|
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="disable"
|
.clickAction=${this._onDisable}
|
||||||
.disabled=${this.disabled || type === "list"}
|
.disabled=${this.disabled || type === "list"}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${"enabled" in this.trigger && this.trigger.enabled === false
|
.path=${"enabled" in this.trigger && this.trigger.enabled === false
|
||||||
? mdiPlayCircleOutline
|
? mdiPlayCircleOutline
|
||||||
: mdiStopCircleOutline}
|
: mdiStopCircleOutline}
|
||||||
@@ -345,15 +358,15 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
`ui.panel.config.automation.editor.actions.${"enabled" in this.trigger && this.trigger.enabled === false ? "enable" : "disable"}`
|
`ui.panel.config.automation.editor.actions.${"enabled" in this.trigger && this.trigger.enabled === false ? "enable" : "disable"}`
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="delete"
|
.clickAction=${this._onDelete}
|
||||||
variant="danger"
|
class="warning"
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class="warning"
|
class="warning"
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${mdiDelete}
|
.path=${mdiDelete}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this._renderOverflowLabel(
|
${this._renderOverflowLabel(
|
||||||
@@ -364,6 +377,7 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -378,8 +392,8 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
>
|
>
|
||||||
</span>`
|
</span>`
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
</ha-dropdown>
|
</ha-md-button-menu>
|
||||||
${!this.optionsInSidebar
|
${!this.optionsInSidebar
|
||||||
? html`${this._warnings
|
? html`${this._warnings
|
||||||
? html`<ha-automation-editor-warning
|
? html`<ha-automation-editor-warning
|
||||||
@@ -790,44 +804,6 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
this._automationRowElement?.focus();
|
this._automationRowElement?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "rename":
|
|
||||||
this._renameTrigger();
|
|
||||||
break;
|
|
||||||
case "duplicate":
|
|
||||||
this._duplicateTrigger();
|
|
||||||
break;
|
|
||||||
case "copy":
|
|
||||||
this._copyTrigger();
|
|
||||||
break;
|
|
||||||
case "cut":
|
|
||||||
this._cutTrigger();
|
|
||||||
break;
|
|
||||||
case "move_up":
|
|
||||||
this._moveUp();
|
|
||||||
break;
|
|
||||||
case "move_down":
|
|
||||||
this._moveDown();
|
|
||||||
break;
|
|
||||||
case "toggle_yaml_mode":
|
|
||||||
this._toggleYamlMode(ev.target as HTMLElement);
|
|
||||||
break;
|
|
||||||
case "disable":
|
|
||||||
this._onDisable();
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
this._onDelete();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
rowStyles,
|
rowStyles,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import {
|
|||||||
type Trigger,
|
type Trigger,
|
||||||
type TriggerList,
|
type TriggerList,
|
||||||
} from "../../../../data/automation";
|
} from "../../../../data/automation";
|
||||||
import { subscribeLabFeature } from "../../../../data/labs";
|
import { subscribeLabFeatures } from "../../../../data/labs";
|
||||||
import type { TriggerDescriptions } from "../../../../data/trigger";
|
import type { TriggerDescriptions } from "../../../../data/trigger";
|
||||||
import { isTriggerList, subscribeTriggers } from "../../../../data/trigger";
|
import { isTriggerList, subscribeTriggers } from "../../../../data/trigger";
|
||||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||||
@@ -85,14 +85,14 @@ export default class HaAutomationTrigger extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
protected hassSubscribe() {
|
protected hassSubscribe() {
|
||||||
return [
|
return [
|
||||||
subscribeLabFeature(
|
subscribeLabFeatures(this.hass!.connection, (features) => {
|
||||||
this.hass!.connection,
|
this._newTriggersAndConditions =
|
||||||
"automation",
|
features.find(
|
||||||
"new_triggers_conditions",
|
(feature) =>
|
||||||
(feature) => {
|
feature.domain === "automation" &&
|
||||||
this._newTriggersAndConditions = feature.enabled;
|
feature.preview_feature === "new_triggers_conditions"
|
||||||
}
|
)?.enabled ?? false;
|
||||||
),
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
|
import {
|
||||||
import { mdiDotsVertical, mdiRefresh } from "@mdi/js";
|
mdiCheckboxBlankOutline,
|
||||||
|
mdiCheckboxMarked,
|
||||||
|
mdiDotsVertical,
|
||||||
|
mdiLocationEnter,
|
||||||
|
mdiLocationExit,
|
||||||
|
mdiRefresh,
|
||||||
|
} from "@mdi/js";
|
||||||
import type { HassEntities } from "home-assistant-js-websocket";
|
import type { HassEntities } from "home-assistant-js-websocket";
|
||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { LitElement, css, html } from "lit";
|
import { LitElement, css, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
|
|
||||||
import "../../../components/ha-alert";
|
import "../../../components/ha-alert";
|
||||||
import "../../../components/ha-bar";
|
import "../../../components/ha-bar";
|
||||||
import "../../../components/ha-button-menu";
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-check-list-item";
|
|
||||||
import "../../../components/ha-list-item";
|
|
||||||
import "../../../components/ha-metric";
|
import "../../../components/ha-metric";
|
||||||
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
||||||
import type {
|
import type {
|
||||||
@@ -33,6 +35,9 @@ import "../../../layouts/hass-subpage";
|
|||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import "../dashboard/ha-config-updates";
|
import "../dashboard/ha-config-updates";
|
||||||
import { showJoinBetaDialog } from "./updates/show-dialog-join-beta";
|
import { showJoinBetaDialog } from "./updates/show-dialog-join-beta";
|
||||||
|
import "../../../components/ha-dropdown";
|
||||||
|
import "../../../components/ha-dropdown-item";
|
||||||
|
import "@home-assistant/webawesome/dist/components/divider/divider";
|
||||||
|
|
||||||
@customElement("ha-config-section-updates")
|
@customElement("ha-config-section-updates")
|
||||||
class HaConfigSectionUpdates extends LitElement {
|
class HaConfigSectionUpdates extends LitElement {
|
||||||
@@ -73,35 +78,44 @@ class HaConfigSectionUpdates extends LitElement {
|
|||||||
.path=${mdiRefresh}
|
.path=${mdiRefresh}
|
||||||
@click=${this._checkUpdates}
|
@click=${this._checkUpdates}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-button-menu multi>
|
<ha-dropdown @wa-select=${this._handleOverflowAction}>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-check-list-item
|
|
||||||
left
|
<ha-dropdown-item value="show_skipped">
|
||||||
@request-selected=${this._toggleSkipped}
|
<ha-svg-icon
|
||||||
.selected=${this._showSkipped}
|
.path=${this._showSkipped
|
||||||
>
|
? mdiCheckboxMarked
|
||||||
|
: mdiCheckboxBlankOutline}
|
||||||
|
slot="icon"
|
||||||
|
></ha-svg-icon>
|
||||||
${this.hass.localize("ui.panel.config.updates.show_skipped")}
|
${this.hass.localize("ui.panel.config.updates.show_skipped")}
|
||||||
</ha-check-list-item>
|
</ha-dropdown-item>
|
||||||
${this._supervisorInfo
|
${this._supervisorInfo
|
||||||
? html`
|
? html`
|
||||||
<li divider role="separator"></li>
|
<wa-divider></wa-divider>
|
||||||
<ha-list-item
|
<ha-dropdown-item
|
||||||
@request-selected=${this._toggleBeta}
|
value="toggle_beta"
|
||||||
.disabled=${this._supervisorInfo.channel === "dev"}
|
.disabled=${this._supervisorInfo.channel === "dev"}
|
||||||
>
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
.path=${this._supervisorInfo.channel === "stable"
|
||||||
|
? mdiLocationEnter
|
||||||
|
: mdiLocationExit}
|
||||||
|
slot="icon"
|
||||||
|
></ha-svg-icon>
|
||||||
${this._supervisorInfo.channel === "stable"
|
${this._supervisorInfo.channel === "stable"
|
||||||
? this.hass.localize("ui.panel.config.updates.join_beta")
|
? this.hass.localize("ui.panel.config.updates.join_beta")
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
"ui.panel.config.updates.leave_beta"
|
"ui.panel.config.updates.leave_beta"
|
||||||
)}
|
)}
|
||||||
</ha-list-item>
|
</ha-dropdown-item>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-button-menu>
|
</ha-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<ha-card outlined>
|
<ha-card outlined>
|
||||||
@@ -133,21 +147,10 @@ class HaConfigSectionUpdates extends LitElement {
|
|||||||
this._supervisorInfo = await fetchHassioSupervisorInfo(this.hass);
|
this._supervisorInfo = await fetchHassioSupervisorInfo(this.hass);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleSkipped(ev: CustomEvent<RequestSelectedDetail>): void {
|
private async _handleOverflowAction(
|
||||||
if (ev.detail.source !== "property") {
|
ev: CustomEvent<{ item: { value: string } }>
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._showSkipped = !this._showSkipped;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _toggleBeta(
|
|
||||||
ev: CustomEvent<RequestSelectedDetail>
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!shouldHandleRequestSelectedEvent(ev)) {
|
if (ev.detail.item.value === "toggle_beta") {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._supervisorInfo!.channel === "stable") {
|
if (this._supervisorInfo!.channel === "stable") {
|
||||||
showJoinBetaDialog(this, {
|
showJoinBetaDialog(this, {
|
||||||
join: async () => this._setChannel("beta"),
|
join: async () => this._setChannel("beta"),
|
||||||
@@ -155,6 +158,9 @@ class HaConfigSectionUpdates extends LitElement {
|
|||||||
} else {
|
} else {
|
||||||
this._setChannel("stable");
|
this._setChannel("stable");
|
||||||
}
|
}
|
||||||
|
} else if (ev.detail.item.value === "show_skipped") {
|
||||||
|
this._showSkipped = !this._showSkipped;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _setChannel(
|
private async _setChannel(
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import {
|
|||||||
} from "../../../dialogs/quick-bar/show-dialog-quick-bar";
|
} from "../../../dialogs/quick-bar/show-dialog-quick-bar";
|
||||||
import { showRestartDialog } from "../../../dialogs/restart/show-dialog-restart";
|
import { showRestartDialog } from "../../../dialogs/restart/show-dialog-restart";
|
||||||
import { showShortcutsDialog } from "../../../dialogs/shortcuts/show-shortcuts-dialog";
|
import { showShortcutsDialog } from "../../../dialogs/shortcuts/show-shortcuts-dialog";
|
||||||
|
import type { PageNavigation } from "../../../layouts/hass-tabs-subpage";
|
||||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
@@ -163,10 +164,10 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
|||||||
total: 0,
|
total: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
private _pages = memoizeOne((cloudStatus, isCloudLoaded) => [
|
private _pages = memoizeOne((cloudStatus, isCloudLoaded) => {
|
||||||
isCloudLoaded
|
const pages: PageNavigation[] = [];
|
||||||
? [
|
if (isCloudLoaded) {
|
||||||
{
|
pages.push({
|
||||||
component: "cloud",
|
component: "cloud",
|
||||||
path: "/config/cloud",
|
path: "/config/cloud",
|
||||||
name: "Home Assistant Cloud",
|
name: "Home Assistant Cloud",
|
||||||
@@ -174,13 +175,10 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
|||||||
iconPath: mdiCloudLock,
|
iconPath: mdiCloudLock,
|
||||||
iconColor: "#3B808E",
|
iconColor: "#3B808E",
|
||||||
translationKey: "cloud",
|
translationKey: "cloud",
|
||||||
},
|
});
|
||||||
...configSections.dashboard,
|
}
|
||||||
]
|
return [...pages, ...configSections.dashboard];
|
||||||
: configSections.dashboard,
|
});
|
||||||
configSections.dashboard_2,
|
|
||||||
configSections.dashboard_3,
|
|
||||||
]);
|
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
return [
|
return [
|
||||||
@@ -310,21 +308,18 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
|||||||
: ""}
|
: ""}
|
||||||
</ha-card>`
|
</ha-card>`
|
||||||
: ""}
|
: ""}
|
||||||
${this._pages(
|
|
||||||
this.cloudStatus,
|
|
||||||
isComponentLoaded(this.hass, "cloud")
|
|
||||||
).map(
|
|
||||||
(categoryPages) => html`
|
|
||||||
<ha-card outlined>
|
<ha-card outlined>
|
||||||
<ha-config-navigation
|
<ha-config-navigation
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.showAdvanced=${this.showAdvanced}
|
.showAdvanced=${this.showAdvanced}
|
||||||
.pages=${categoryPages}
|
.pages=${this._pages(
|
||||||
|
this.cloudStatus,
|
||||||
|
isComponentLoaded(this.hass, "cloud")
|
||||||
|
)}
|
||||||
></ha-config-navigation>
|
></ha-config-navigation>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`
|
|
||||||
)}
|
|
||||||
<ha-tip .hass=${this.hass}>${this._tip}</ha-tip>
|
<ha-tip .hass=${this.hass}>${this._tip}</ha-tip>
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
</ha-top-app-bar-fixed>
|
</ha-top-app-bar-fixed>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import "../../../../components/ha-dialog";
|
|||||||
import "../../../../components/ha-formfield";
|
import "../../../../components/ha-formfield";
|
||||||
import "../../../../components/ha-radio";
|
import "../../../../components/ha-radio";
|
||||||
import "../../../../components/ha-button";
|
import "../../../../components/ha-button";
|
||||||
import "../../../../components/ha-markdown";
|
|
||||||
import type { HaRadio } from "../../../../components/ha-radio";
|
import type { HaRadio } from "../../../../components/ha-radio";
|
||||||
import "../../../../components/ha-textfield";
|
import "../../../../components/ha-textfield";
|
||||||
import type { GasSourceTypeEnergyPreference } from "../../../../data/energy";
|
import type { GasSourceTypeEnergyPreference } from "../../../../data/energy";
|
||||||
@@ -110,15 +109,6 @@ export class DialogEnergyGasSettings
|
|||||||
? `${this.hass.config.currency}/${this._pickedDisplayUnit}`
|
? `${this.hass.config.currency}/${this._pickedDisplayUnit}`
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const pickedUnitClass =
|
|
||||||
this._pickedDisplayUnit &&
|
|
||||||
this._energy_units?.includes(this._pickedDisplayUnit)
|
|
||||||
? "energy"
|
|
||||||
: this._pickedDisplayUnit &&
|
|
||||||
this._gas_units?.includes(this._pickedDisplayUnit)
|
|
||||||
? "volume"
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const externalSource =
|
const externalSource =
|
||||||
this._source.stat_energy_from &&
|
this._source.stat_energy_from &&
|
||||||
isExternalStatistic(this._source.stat_energy_from);
|
isExternalStatistic(this._source.stat_energy_from);
|
||||||
@@ -223,33 +213,9 @@ export class DialogEnergyGasSettings
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
include-domains='["sensor", "input_number"]'
|
include-domains='["sensor", "input_number"]'
|
||||||
.value=${this._source.entity_energy_price}
|
.value=${this._source.entity_energy_price}
|
||||||
.label=${this.hass.localize(
|
.label=${`${this.hass.localize(
|
||||||
"ui.panel.config.energy.gas.dialog.cost_entity_input"
|
"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}
|
@value-changed=${this._priceEntityChanged}
|
||||||
></ha-entity-picker>`
|
></ha-entity-picker>`
|
||||||
: ""}
|
: ""}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import "../../../../components/ha-dialog";
|
|||||||
import "../../../../components/ha-button";
|
import "../../../../components/ha-button";
|
||||||
import "../../../../components/ha-formfield";
|
import "../../../../components/ha-formfield";
|
||||||
import "../../../../components/ha-radio";
|
import "../../../../components/ha-radio";
|
||||||
import "../../../../components/ha-markdown";
|
|
||||||
import type { HaRadio } from "../../../../components/ha-radio";
|
import type { HaRadio } from "../../../../components/ha-radio";
|
||||||
import "../../../../components/ha-textfield";
|
import "../../../../components/ha-textfield";
|
||||||
import type { WaterSourceTypeEnergyPreference } from "../../../../data/energy";
|
import type { WaterSourceTypeEnergyPreference } from "../../../../data/energy";
|
||||||
@@ -17,7 +16,11 @@ import {
|
|||||||
emptyWaterEnergyPreference,
|
emptyWaterEnergyPreference,
|
||||||
energyStatisticHelpUrl,
|
energyStatisticHelpUrl,
|
||||||
} from "../../../../data/energy";
|
} from "../../../../data/energy";
|
||||||
import { isExternalStatistic } from "../../../../data/recorder";
|
import {
|
||||||
|
getDisplayUnit,
|
||||||
|
getStatisticMetadata,
|
||||||
|
isExternalStatistic,
|
||||||
|
} from "../../../../data/recorder";
|
||||||
import { getSensorDeviceClassConvertibleUnits } from "../../../../data/sensor";
|
import { getSensorDeviceClassConvertibleUnits } from "../../../../data/sensor";
|
||||||
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
|
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
|
||||||
import { haStyle, haStyleDialog } from "../../../../resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../resources/styles";
|
||||||
@@ -37,6 +40,8 @@ export class DialogEnergyWaterSettings
|
|||||||
|
|
||||||
@state() private _costs?: "no-costs" | "number" | "entity" | "statistic";
|
@state() private _costs?: "no-costs" | "number" | "entity" | "statistic";
|
||||||
|
|
||||||
|
@state() private _pickedDisplayUnit?: string | null;
|
||||||
|
|
||||||
@state() private _water_units?: string[];
|
@state() private _water_units?: string[];
|
||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
@@ -50,6 +55,11 @@ export class DialogEnergyWaterSettings
|
|||||||
this._source = params.source
|
this._source = params.source
|
||||||
? { ...params.source }
|
? { ...params.source }
|
||||||
: emptyWaterEnergyPreference();
|
: emptyWaterEnergyPreference();
|
||||||
|
this._pickedDisplayUnit = getDisplayUnit(
|
||||||
|
this.hass,
|
||||||
|
params.source?.stat_energy_from,
|
||||||
|
params.metadata
|
||||||
|
);
|
||||||
this._costs = this._source.entity_energy_price
|
this._costs = this._source.entity_energy_price
|
||||||
? "entity"
|
? "entity"
|
||||||
: this._source.number_energy_price
|
: this._source.number_energy_price
|
||||||
@@ -69,6 +79,7 @@ export class DialogEnergyWaterSettings
|
|||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
this._source = undefined;
|
this._source = undefined;
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
|
this._pickedDisplayUnit = undefined;
|
||||||
this._excludeList = undefined;
|
this._excludeList = undefined;
|
||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
return true;
|
return true;
|
||||||
@@ -81,6 +92,10 @@ export class DialogEnergyWaterSettings
|
|||||||
|
|
||||||
const pickableUnit = this._water_units?.join(", ") || "";
|
const pickableUnit = this._water_units?.join(", ") || "";
|
||||||
|
|
||||||
|
const unitPriceSensor = this._pickedDisplayUnit
|
||||||
|
? `${this.hass.config.currency}/${this._pickedDisplayUnit}`
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const unitPriceFixed = `${this.hass.config.currency}/${
|
const unitPriceFixed = `${this.hass.config.currency}/${
|
||||||
this.hass.config.unit_system.volume === "gal" ? "gal" : "m³"
|
this.hass.config.unit_system.volume === "gal" ? "gal" : "m³"
|
||||||
}`;
|
}`;
|
||||||
@@ -187,15 +202,9 @@ export class DialogEnergyWaterSettings
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
include-domains='["sensor", "input_number"]'
|
include-domains='["sensor", "input_number"]'
|
||||||
.value=${this._source.entity_energy_price}
|
.value=${this._source.entity_energy_price}
|
||||||
.label=${this.hass.localize(
|
.label=${`${this.hass.localize(
|
||||||
"ui.panel.config.energy.water.dialog.cost_entity_input"
|
"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}
|
@value-changed=${this._priceEntityChanged}
|
||||||
></ha-entity-picker>`
|
></ha-entity-picker>`
|
||||||
: ""}
|
: ""}
|
||||||
@@ -278,6 +287,16 @@ export class DialogEnergyWaterSettings
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _statisticChanged(ev: CustomEvent<{ value: string }>) {
|
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") {
|
if (isExternalStatistic(ev.detail.value) && this._costs !== "statistic") {
|
||||||
this._costs = "no-costs";
|
this._costs = "no-costs";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1525,17 +1525,17 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
ha-textfield.entityId ha-icon-button {
|
ha-textfield.entityId ha-icon-button {
|
||||||
position: relative;
|
position: relative;
|
||||||
right: calc(var(--ha-space-2) * -1);
|
right: -8px;
|
||||||
--mdc-icon-button-size: 36px;
|
--mdc-icon-button-size: 36px;
|
||||||
--mdc-icon-size: 20px;
|
--mdc-icon-size: 20px;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
inset-inline-start: initial;
|
inset-inline-start: initial;
|
||||||
inset-inline-end: calc(var(--ha-space-2) * -1);
|
inset-inline-end: -8px;
|
||||||
direction: var(--direction);
|
direction: var(--direction);
|
||||||
}
|
}
|
||||||
ha-switch {
|
ha-switch {
|
||||||
margin-right: var(--ha-space-4);
|
margin-right: 16px;
|
||||||
margin-inline-end: var(--ha-space-4);
|
margin-inline-end: 16px;
|
||||||
margin-inline-start: initial;
|
margin-inline-start: initial;
|
||||||
}
|
}
|
||||||
ha-settings-row ha-switch {
|
ha-settings-row ha-switch {
|
||||||
@@ -1548,7 +1548,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
|||||||
ha-select,
|
ha-select,
|
||||||
ha-area-picker {
|
ha-area-picker {
|
||||||
display: block;
|
display: block;
|
||||||
margin: var(--ha-space-2) 0;
|
margin: 8px 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
li[divider] {
|
li[divider] {
|
||||||
|
|||||||
@@ -244,15 +244,14 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.container {
|
.container {
|
||||||
padding: var(--ha-space-2) var(--ha-space-6) var(--ha-space-5)
|
padding: 8px 24px 20px 24px;
|
||||||
var(--ha-space-6);
|
|
||||||
}
|
}
|
||||||
.buttons {
|
.buttons {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: var(--ha-space-4);
|
padding: 16px;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding-bottom: max(var(--safe-area-inset-bottom), var(--ha-space-4));
|
padding-bottom: max(var(--safe-area-inset-bottom), 16px);
|
||||||
background-color: var(--mdc-theme-surface, #fff);
|
background-color: var(--mdc-theme-surface, #fff);
|
||||||
border-top: 1px solid var(--divider-color);
|
border-top: 1px solid var(--divider-color);
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
|||||||
@@ -3,17 +3,14 @@ import {
|
|||||||
mdiAccount,
|
mdiAccount,
|
||||||
mdiBackupRestore,
|
mdiBackupRestore,
|
||||||
mdiBadgeAccountHorizontal,
|
mdiBadgeAccountHorizontal,
|
||||||
mdiBluetooth,
|
|
||||||
mdiCellphoneCog,
|
mdiCellphoneCog,
|
||||||
mdiCog,
|
mdiCog,
|
||||||
mdiDatabase,
|
mdiDatabase,
|
||||||
mdiDevices,
|
mdiDevices,
|
||||||
mdiFlask,
|
mdiFlask,
|
||||||
mdiHub,
|
|
||||||
mdiInformation,
|
mdiInformation,
|
||||||
mdiInformationOutline,
|
mdiInformationOutline,
|
||||||
mdiLabel,
|
mdiLabel,
|
||||||
mdiLan,
|
|
||||||
mdiLightningBolt,
|
mdiLightningBolt,
|
||||||
mdiMapMarkerRadius,
|
mdiMapMarkerRadius,
|
||||||
mdiMathLog,
|
mdiMathLog,
|
||||||
@@ -23,19 +20,15 @@ import {
|
|||||||
mdiNfcVariant,
|
mdiNfcVariant,
|
||||||
mdiPalette,
|
mdiPalette,
|
||||||
mdiPaletteSwatch,
|
mdiPaletteSwatch,
|
||||||
mdiProtocol,
|
|
||||||
mdiPuzzle,
|
mdiPuzzle,
|
||||||
mdiRobot,
|
mdiRobot,
|
||||||
mdiScrewdriver,
|
mdiScrewdriver,
|
||||||
mdiScriptText,
|
mdiScriptText,
|
||||||
mdiShape,
|
mdiShape,
|
||||||
mdiSofa,
|
mdiSofa,
|
||||||
mdiStore,
|
|
||||||
mdiTools,
|
mdiTools,
|
||||||
mdiUpdate,
|
mdiUpdate,
|
||||||
mdiViewDashboard,
|
mdiViewDashboard,
|
||||||
mdiZigbee,
|
|
||||||
mdiZWave,
|
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import type { PropertyValues } from "lit";
|
import type { PropertyValues } from "lit";
|
||||||
@@ -108,72 +101,6 @@ export const configSections: Record<string, PageNavigation[]> = {
|
|||||||
iconPath: mdiMicrophone,
|
iconPath: mdiMicrophone,
|
||||||
iconColor: "#3263C3",
|
iconColor: "#3263C3",
|
||||||
},
|
},
|
||||||
],
|
|
||||||
dashboard_2: [
|
|
||||||
{
|
|
||||||
path: "/config/zwave_js",
|
|
||||||
name: "Z-Wave",
|
|
||||||
iconPath: mdiZWave,
|
|
||||||
iconColor: "#153163",
|
|
||||||
component: "zwave_js",
|
|
||||||
translationKey: "zwave_js",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/config/zha",
|
|
||||||
name: "Zigbee",
|
|
||||||
iconPath: mdiZigbee,
|
|
||||||
iconColor: "#E74011",
|
|
||||||
component: "zha",
|
|
||||||
translationKey: "zha",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/config/matter",
|
|
||||||
name: "Matter",
|
|
||||||
iconPath: mdiHub,
|
|
||||||
iconColor: "#2458B3",
|
|
||||||
component: "matter",
|
|
||||||
translationKey: "matter",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/config/thread",
|
|
||||||
name: "Thread",
|
|
||||||
iconPath: mdiProtocol,
|
|
||||||
iconColor: "#ED7744",
|
|
||||||
component: "thread",
|
|
||||||
translationKey: "thread",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/config/bluetooth",
|
|
||||||
name: "Bluetooth",
|
|
||||||
iconPath: mdiBluetooth,
|
|
||||||
iconColor: "#0082FC",
|
|
||||||
component: "bluetooth",
|
|
||||||
translationKey: "bluetooth",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/knx",
|
|
||||||
name: "KNX",
|
|
||||||
iconPath: mdiLan,
|
|
||||||
iconColor: "#4EAA66",
|
|
||||||
component: "knx",
|
|
||||||
translationKey: "knx",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/insteon",
|
|
||||||
name: "Insteon",
|
|
||||||
iconPath: mdiLan,
|
|
||||||
iconColor: "#E4002C",
|
|
||||||
component: "insteon",
|
|
||||||
translationKey: "insteon",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/hacs",
|
|
||||||
name: "Home Assistant Community Store",
|
|
||||||
iconPath: mdiStore,
|
|
||||||
iconColor: "#41BDF5",
|
|
||||||
component: "hacs",
|
|
||||||
translationKey: "hacs",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "/config/tags",
|
path: "/config/tags",
|
||||||
translationKey: "tags",
|
translationKey: "tags",
|
||||||
@@ -181,8 +108,6 @@ export const configSections: Record<string, PageNavigation[]> = {
|
|||||||
iconColor: "#616161",
|
iconColor: "#616161",
|
||||||
component: "tag",
|
component: "tag",
|
||||||
},
|
},
|
||||||
],
|
|
||||||
dashboard_3: [
|
|
||||||
{
|
{
|
||||||
path: "/config/person",
|
path: "/config/person",
|
||||||
translationKey: "people",
|
translationKey: "people",
|
||||||
|
|||||||
@@ -336,7 +336,9 @@ class HaConfigIntegrationsDashboard extends KeyboardShortcutMixin(
|
|||||||
super.firstUpdated(changed);
|
super.firstUpdated(changed);
|
||||||
this._fetchManifests();
|
this._fetchManifests();
|
||||||
this._fetchEntitySources();
|
this._fetchEntitySources();
|
||||||
this._handleRouteChanged();
|
if (this.route.path === "/add") {
|
||||||
|
this._handleAdd();
|
||||||
|
}
|
||||||
this._scanUSBDevices();
|
this._scanUSBDevices();
|
||||||
this._scanImprovDevices();
|
this._scanImprovDevices();
|
||||||
|
|
||||||
@@ -353,9 +355,6 @@ class HaConfigIntegrationsDashboard extends KeyboardShortcutMixin(
|
|||||||
|
|
||||||
protected updated(changed: PropertyValues) {
|
protected updated(changed: PropertyValues) {
|
||||||
super.updated(changed);
|
super.updated(changed);
|
||||||
if (changed.has("route")) {
|
|
||||||
this._handleRouteChanged();
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
(this._searchParms.has("config_entry") ||
|
(this._searchParms.has("config_entry") ||
|
||||||
this._searchParms.has("domain")) &&
|
this._searchParms.has("domain")) &&
|
||||||
@@ -814,13 +813,10 @@ class HaConfigIntegrationsDashboard extends KeyboardShortcutMixin(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _handleRouteChanged() {
|
private async _handleAdd() {
|
||||||
if (this.route?.path !== "/add") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const brand = extractSearchParam("brand");
|
const brand = extractSearchParam("brand");
|
||||||
const domain = extractSearchParam("domain");
|
const domain = extractSearchParam("domain");
|
||||||
navigate("/config/integrations/dashboard/", { replace: true });
|
navigate("/config/integrations", { replace: true });
|
||||||
|
|
||||||
if (brand) {
|
if (brand) {
|
||||||
showAddIntegrationDialog(this, {
|
showAddIntegrationDialog(this, {
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ export class MatterAddDevice extends HTMLElement {
|
|||||||
public hass!: HomeAssistant;
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
navigate("/config/devices/dashboard", {
|
showMatterAddDeviceDialog(this);
|
||||||
|
navigate(`/config/devices`, {
|
||||||
replace: true,
|
replace: true,
|
||||||
});
|
});
|
||||||
showMatterAddDeviceDialog(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,10 @@ import { css, html, LitElement } from "lit";
|
|||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
import { navigate } from "../../../common/navigate";
|
import { navigate } from "../../../common/navigate";
|
||||||
import { stringCompare } from "../../../common/string/compare";
|
|
||||||
import { extractSearchParam } from "../../../common/url/search-params";
|
import { extractSearchParam } from "../../../common/url/search-params";
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
import "../../../components/ha-dropdown";
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-dropdown-item";
|
import "../../../components/ha-list-item";
|
||||||
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
|
|
||||||
import "../../../components/search-input";
|
import "../../../components/search-input";
|
||||||
import type { LogProvider } from "../../../data/error_log";
|
import type { LogProvider } from "../../../data/error_log";
|
||||||
import { fetchHassioAddonsInfo } from "../../../data/hassio/addon";
|
import { fetchHassioAddonsInfo } from "../../../data/hassio/addon";
|
||||||
@@ -20,6 +18,7 @@ import type { HomeAssistant, Route } from "../../../types";
|
|||||||
import "./error-log-card";
|
import "./error-log-card";
|
||||||
import "./system-log-card";
|
import "./system-log-card";
|
||||||
import type { SystemLogCard } from "./system-log-card";
|
import type { SystemLogCard } from "./system-log-card";
|
||||||
|
import { stringCompare } from "../../../common/string/compare";
|
||||||
|
|
||||||
const logProviders: LogProvider[] = [
|
const logProviders: LogProvider[] = [
|
||||||
{
|
{
|
||||||
@@ -118,10 +117,7 @@ export class HaConfigLogs extends LitElement {
|
|||||||
>
|
>
|
||||||
${isComponentLoaded(this.hass, "hassio")
|
${isComponentLoaded(this.hass, "hassio")
|
||||||
? html`
|
? html`
|
||||||
<ha-dropdown
|
<ha-button-menu slot="toolbar-icon">
|
||||||
slot="toolbar-icon"
|
|
||||||
@wa-select=${this._handleDropdownSelect}
|
|
||||||
>
|
|
||||||
<ha-button slot="trigger" appearance="filled">
|
<ha-button slot="trigger" appearance="filled">
|
||||||
<ha-svg-icon slot="end" .path=${mdiChevronDown}></ha-svg-icon>
|
<ha-svg-icon slot="end" .path=${mdiChevronDown}></ha-svg-icon>
|
||||||
${this._logProviders.find(
|
${this._logProviders.find(
|
||||||
@@ -130,17 +126,16 @@ export class HaConfigLogs extends LitElement {
|
|||||||
</ha-button>
|
</ha-button>
|
||||||
${this._logProviders.map(
|
${this._logProviders.map(
|
||||||
(provider) => html`
|
(provider) => html`
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
.value=${provider.key}
|
?selected=${provider.key === this._selectedLogProvider}
|
||||||
class=${provider.key === this._selectedLogProvider
|
.provider=${provider.key}
|
||||||
? "selected"
|
@click=${this._selectProvider}
|
||||||
: ""}
|
|
||||||
>
|
>
|
||||||
${provider.name}
|
${provider.name}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</ha-dropdown>
|
</ha-button-menu>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${search}
|
${search}
|
||||||
@@ -175,12 +170,8 @@ export class HaConfigLogs extends LitElement {
|
|||||||
this._detail = !this._detail;
|
this._detail = !this._detail;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
private _selectProvider(ev) {
|
||||||
const provider = ev.detail?.item?.value;
|
this._selectedLogProvider = (ev.currentTarget as any).provider;
|
||||||
if (!provider) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._selectedLogProvider = provider;
|
|
||||||
this._filter = "";
|
this._filter = "";
|
||||||
navigate(`/config/logs?provider=${this._selectedLogProvider}`);
|
navigate(`/config/logs?provider=${this._selectedLogProvider}`);
|
||||||
}
|
}
|
||||||
@@ -263,7 +254,7 @@ export class HaConfigLogs extends LitElement {
|
|||||||
direction: ltr;
|
direction: ltr;
|
||||||
}
|
}
|
||||||
@media all and (max-width: 870px) {
|
@media all and (max-width: 870px) {
|
||||||
ha-dropdown {
|
ha-button-menu {
|
||||||
max-width: 50%;
|
max-width: 50%;
|
||||||
}
|
}
|
||||||
ha-button {
|
ha-button {
|
||||||
@@ -274,8 +265,8 @@ export class HaConfigLogs extends LitElement {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ha-dropdown-item.selected {
|
ha-list-item[selected] {
|
||||||
font-weight: var(--ha-font-weight-bold);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
|
||||||
import { consume } from "@lit/context";
|
import { consume } from "@lit/context";
|
||||||
import {
|
import {
|
||||||
mdiAppleKeyboardCommand,
|
mdiAppleKeyboardCommand,
|
||||||
@@ -24,19 +23,18 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
|||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { UndoRedoController } from "../../../common/controllers/undo-redo-controller";
|
|
||||||
import { transform } from "../../../common/decorators/transform";
|
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { goBack, navigate } from "../../../common/navigate";
|
import { goBack, navigate } from "../../../common/navigate";
|
||||||
import { slugify } from "../../../common/string/slugify";
|
import { slugify } from "../../../common/string/slugify";
|
||||||
import { promiseTimeout } from "../../../common/util/promise-timeout";
|
import { promiseTimeout } from "../../../common/util/promise-timeout";
|
||||||
import { afterNextRender } from "../../../common/util/render-status";
|
import { afterNextRender } from "../../../common/util/render-status";
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
import "../../../components/ha-dropdown";
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
|
|
||||||
import "../../../components/ha-fab";
|
import "../../../components/ha-fab";
|
||||||
|
|
||||||
|
import { transform } from "../../../common/decorators/transform";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
|
import "../../../components/ha-list-item";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import "../../../components/ha-yaml-editor";
|
import "../../../components/ha-yaml-editor";
|
||||||
import { substituteBlueprint } from "../../../data/blueprint";
|
import { substituteBlueprint } from "../../../data/blueprint";
|
||||||
@@ -67,6 +65,7 @@ import "../../../layouts/hass-subpage";
|
|||||||
import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin";
|
import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin";
|
||||||
import { PreventUnsavedMixin } from "../../../mixins/prevent-unsaved-mixin";
|
import { PreventUnsavedMixin } from "../../../mixins/prevent-unsaved-mixin";
|
||||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
|
import { UndoRedoController } from "../../../common/controllers/undo-redo-controller";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import type { Entries, HomeAssistant, Route } from "../../../types";
|
import type { Entries, HomeAssistant, Route } from "../../../types";
|
||||||
import { isMac } from "../../../util/is_mac";
|
import { isMac } from "../../../util/is_mac";
|
||||||
@@ -74,11 +73,11 @@ import { showToast } from "../../../util/toast";
|
|||||||
import { showAutomationModeDialog } from "../automation/automation-mode-dialog/show-dialog-automation-mode";
|
import { showAutomationModeDialog } from "../automation/automation-mode-dialog/show-dialog-automation-mode";
|
||||||
import type { EntityRegistryUpdate } from "../automation/automation-save-dialog/show-dialog-automation-save";
|
import type { EntityRegistryUpdate } from "../automation/automation-save-dialog/show-dialog-automation-save";
|
||||||
import { showAutomationSaveDialog } from "../automation/automation-save-dialog/show-dialog-automation-save";
|
import { showAutomationSaveDialog } from "../automation/automation-save-dialog/show-dialog-automation-save";
|
||||||
import { showAutomationSaveTimeoutDialog } from "../automation/automation-save-timeout-dialog/show-dialog-automation-save-timeout";
|
|
||||||
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
||||||
import "./blueprint-script-editor";
|
import "./blueprint-script-editor";
|
||||||
import "./manual-script-editor";
|
import "./manual-script-editor";
|
||||||
import type { HaManualScriptEditor } from "./manual-script-editor";
|
import type { HaManualScriptEditor } from "./manual-script-editor";
|
||||||
|
import { showAutomationSaveTimeoutDialog } from "../automation/automation-save-timeout-dialog/show-dialog-automation-save-timeout";
|
||||||
|
|
||||||
@customElement("ha-script-editor")
|
@customElement("ha-script-editor")
|
||||||
export class HaScriptEditor extends SubscribeMixin(
|
export class HaScriptEditor extends SubscribeMixin(
|
||||||
@@ -242,10 +241,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
|||||||
</ha-button>
|
</ha-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-dropdown
|
<ha-button-menu slot="toolbar-icon">
|
||||||
slot="toolbar-icon"
|
|
||||||
@wa-select=${this._handleDropdownSelect}
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
@@ -253,107 +249,133 @@ export class HaScriptEditor extends SubscribeMixin(
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
|
||||||
${this._mode === "gui" && this.narrow
|
${this._mode === "gui" && this.narrow
|
||||||
? html`<ha-dropdown-item
|
? html`<ha-list-item
|
||||||
value="undo"
|
graphic="icon"
|
||||||
|
@click=${this._undo}
|
||||||
.disabled=${!this._undoRedoController.canUndo}
|
.disabled=${!this._undoRedoController.canUndo}
|
||||||
>
|
>
|
||||||
${this.hass.localize("ui.common.undo")}
|
${this.hass.localize("ui.common.undo")}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiUndo}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiUndo}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
value="redo"
|
graphic="icon"
|
||||||
|
@click=${this._redo}
|
||||||
.disabled=${!this._undoRedoController.canRedo}
|
.disabled=${!this._undoRedoController.canRedo}
|
||||||
>
|
>
|
||||||
${this.hass.localize("ui.common.redo")}
|
${this.hass.localize("ui.common.redo")}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiRedo}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiRedo}></ha-svg-icon>
|
||||||
</ha-dropdown-item>`
|
</ha-list-item>`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-dropdown-item .disabled=${!this.scriptId} value="info">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${!this.scriptId}
|
||||||
|
@click=${this._showInfo}
|
||||||
|
>
|
||||||
${this.hass.localize("ui.panel.config.script.editor.show_info")}
|
${this.hass.localize("ui.panel.config.script.editor.show_info")}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiInformationOutline}
|
.path=${mdiInformationOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
<ha-dropdown-item .disabled=${!stateObj} value="settings">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${!stateObj}
|
||||||
|
@click=${this._showSettings}
|
||||||
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.show_settings"
|
"ui.panel.config.automation.picker.show_settings"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiCog}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiCog}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
<ha-dropdown-item .disabled=${!stateObj} value="category">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${!stateObj}
|
||||||
|
@click=${this._editCategory}
|
||||||
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
`ui.panel.config.scene.picker.${this._registryEntry?.categories?.script ? "edit_category" : "assign_category"}`
|
`ui.panel.config.scene.picker.${this._registryEntry?.categories?.script ? "edit_category" : "assign_category"}`
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiTag}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiTag}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
<ha-dropdown-item .disabled=${!this.scriptId} value="run">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${!this.scriptId}
|
||||||
|
@click=${this._runScript}
|
||||||
|
>
|
||||||
${this.hass.localize("ui.panel.config.script.picker.run_script")}
|
${this.hass.localize("ui.panel.config.script.picker.run_script")}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlay}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiPlay}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
${this.scriptId && this.narrow
|
${this.scriptId && this.narrow
|
||||||
? html`<ha-dropdown-item value="trace">
|
? html`
|
||||||
|
<a href="/config/script/trace/${this.scriptId}">
|
||||||
|
<ha-list-item graphic="icon">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.show_trace"
|
"ui.panel.config.script.editor.show_trace"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiTransitConnection}
|
.path=${mdiTransitConnection}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-dropdown-item>`
|
</ha-list-item>
|
||||||
|
</a>
|
||||||
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
${!useBlueprint && !("fields" in this._config)
|
${!useBlueprint && !("fields" in this._config)
|
||||||
? html`
|
? html`
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
.disabled=${this._readOnly || this._mode === "yaml"}
|
.disabled=${this._readOnly || this._mode === "yaml"}
|
||||||
value="add_fields"
|
@click=${this._addFields}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.script.editor.field.add_fields"
|
"ui.panel.config.script.editor.field.add_fields"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiFormTextbox}
|
.path=${mdiFormTextbox}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
value="rename"
|
graphic="icon"
|
||||||
|
@click=${this._promptScriptAlias}
|
||||||
.disabled=${!this.scriptId ||
|
.disabled=${!this.scriptId ||
|
||||||
this._readOnly ||
|
this._readOnly ||
|
||||||
this._mode === "yaml"}
|
this._mode === "yaml"}
|
||||||
>
|
>
|
||||||
${this.hass.localize("ui.panel.config.script.editor.rename")}
|
${this.hass.localize("ui.panel.config.script.editor.rename")}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiRenameBox}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
${!useBlueprint
|
${!useBlueprint
|
||||||
? html`
|
? html`
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
value="change_mode"
|
graphic="icon"
|
||||||
|
@click=${this._promptScriptMode}
|
||||||
.disabled=${this._readOnly || this._mode === "yaml"}
|
.disabled=${this._readOnly || this._mode === "yaml"}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.script.editor.change_mode"
|
"ui.panel.config.script.editor.change_mode"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiDebugStepOver}
|
.path=${mdiDebugStepOver}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
.disabled=${!!this._blueprintConfig ||
|
.disabled=${this._blueprintConfig ||
|
||||||
(!this._readOnly && !this.scriptId)}
|
(!this._readOnly && !this.scriptId)}
|
||||||
value="duplicate"
|
graphic="icon"
|
||||||
|
@click=${this._duplicate}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
this._readOnly
|
this._readOnly
|
||||||
@@ -361,48 +383,58 @@ export class HaScriptEditor extends SubscribeMixin(
|
|||||||
: "ui.panel.config.script.editor.duplicate"
|
: "ui.panel.config.script.editor.duplicate"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiPlusCircleMultipleOutline}
|
.path=${mdiPlusCircleMultipleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
${useBlueprint
|
${useBlueprint
|
||||||
? html`
|
? html`
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
value="take_control"
|
graphic="icon"
|
||||||
|
@click=${this._takeControl}
|
||||||
.disabled=${this._readOnly}
|
.disabled=${this._readOnly}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.script.editor.take_control"
|
"ui.panel.config.script.editor.take_control"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiFileEdit}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
</ha-dropdown-item>
|
slot="graphic"
|
||||||
|
.path=${mdiFileEdit}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-list-item>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-dropdown-item value="toggle_yaml_mode">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
@click=${this._mode === "gui"
|
||||||
|
? this._switchYamlMode
|
||||||
|
: this._switchUiMode}
|
||||||
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
`ui.panel.config.automation.editor.edit_${this._mode === "gui" ? "yaml" : "ui"}`
|
`ui.panel.config.automation.editor.edit_${this._mode === "gui" ? "yaml" : "ui"}`
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
<wa-divider></wa-divider>
|
<li divider role="separator"></li>
|
||||||
|
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
.disabled=${this._readOnly || !this.scriptId}
|
.disabled=${this._readOnly || !this.scriptId}
|
||||||
value="delete"
|
class=${classMap({ warning: Boolean(this.scriptId) })}
|
||||||
.variant=${this.scriptId ? "danger" : "default"}
|
graphic="icon"
|
||||||
|
@click=${this._deleteConfirm}
|
||||||
>
|
>
|
||||||
${this.hass.localize("ui.panel.config.script.picker.delete")}
|
${this.hass.localize("ui.panel.config.script.picker.delete")}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class=${classMap({ warning: Boolean(this.scriptId) })}
|
class=${classMap({ warning: Boolean(this.scriptId) })}
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiDelete}
|
.path=${mdiDelete}
|
||||||
>
|
>
|
||||||
</ha-svg-icon>
|
</ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
</ha-dropdown>
|
</ha-button-menu>
|
||||||
<div class=${this._mode === "yaml" ? "yaml-mode" : ""}>
|
<div class=${this._mode === "yaml" ? "yaml-mode" : ""}>
|
||||||
${this._mode === "gui"
|
${this._mode === "gui"
|
||||||
? html`
|
? html`
|
||||||
@@ -656,7 +688,9 @@ export class HaScriptEditor extends SubscribeMixin(
|
|||||||
this._dirty = true;
|
this._dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _runScript() {
|
private async _runScript(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
if (hasScriptFields(this.hass, this._entityId!)) {
|
if (hasScriptFields(this.hass, this._entityId!)) {
|
||||||
showMoreInfoDialog(this, {
|
showMoreInfoDialog(this, {
|
||||||
entityId: this._entityId!,
|
entityId: this._entityId!,
|
||||||
@@ -1121,63 +1155,6 @@ export class HaScriptEditor extends SubscribeMixin(
|
|||||||
this._undoRedoController.redo();
|
this._undoRedoController.redo();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "undo":
|
|
||||||
this._undo();
|
|
||||||
break;
|
|
||||||
case "redo":
|
|
||||||
this._redo();
|
|
||||||
break;
|
|
||||||
case "info":
|
|
||||||
this._showInfo();
|
|
||||||
break;
|
|
||||||
case "settings":
|
|
||||||
this._showSettings();
|
|
||||||
break;
|
|
||||||
case "category":
|
|
||||||
this._editCategory();
|
|
||||||
break;
|
|
||||||
case "run":
|
|
||||||
this._runScript();
|
|
||||||
break;
|
|
||||||
case "add_fields":
|
|
||||||
this._addFields();
|
|
||||||
break;
|
|
||||||
case "rename":
|
|
||||||
this._promptScriptAlias();
|
|
||||||
break;
|
|
||||||
case "change_mode":
|
|
||||||
this._promptScriptMode();
|
|
||||||
break;
|
|
||||||
case "duplicate":
|
|
||||||
this._duplicate();
|
|
||||||
break;
|
|
||||||
case "take_control":
|
|
||||||
this._takeControl();
|
|
||||||
break;
|
|
||||||
case "toggle_yaml_mode":
|
|
||||||
if (this._mode === "gui") {
|
|
||||||
this._switchYamlMode();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this._switchUiMode();
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
this._deleteConfirm();
|
|
||||||
break;
|
|
||||||
case "trace":
|
|
||||||
this._showTrace();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
@@ -1268,6 +1245,9 @@ export class HaScriptEditor extends SubscribeMixin(
|
|||||||
ha-fab.dirty {
|
ha-fab.dirty {
|
||||||
bottom: calc(16px + var(--safe-area-inset-bottom, 0px));
|
bottom: calc(16px + var(--safe-area-inset-bottom, 0px));
|
||||||
}
|
}
|
||||||
|
li[role="separator"] {
|
||||||
|
border-bottom-color: var(--divider-color);
|
||||||
|
}
|
||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 16px 0;
|
margin: 16px 0;
|
||||||
@@ -1281,6 +1261,10 @@ export class HaScriptEditor extends SubscribeMixin(
|
|||||||
.header a {
|
.header a {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
ha-button-menu a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
ha-tooltip ha-svg-icon {
|
ha-tooltip ha-svg-icon {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,19 +15,18 @@ import type { LocalizeKeys } from "../../../common/translations/localize";
|
|||||||
import "../../../components/ha-automation-row";
|
import "../../../components/ha-automation-row";
|
||||||
import type { HaAutomationRow } from "../../../components/ha-automation-row";
|
import type { HaAutomationRow } from "../../../components/ha-automation-row";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-dropdown";
|
import "../../../components/ha-md-button-menu";
|
||||||
import "../../../components/ha-dropdown-item";
|
import "../../../components/ha-md-menu-item";
|
||||||
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
|
|
||||||
import type { ScriptFieldSidebarConfig } from "../../../data/automation";
|
import type { ScriptFieldSidebarConfig } from "../../../data/automation";
|
||||||
import type { Field } from "../../../data/script";
|
import type { Field } from "../../../data/script";
|
||||||
import { SELECTOR_SELECTOR_BUILDING_BLOCKS } from "../../../data/selector/selector_selector";
|
import { SELECTOR_SELECTOR_BUILDING_BLOCKS } from "../../../data/selector/selector_selector";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { isMac } from "../../../util/is_mac";
|
import { isMac } from "../../../util/is_mac";
|
||||||
import { showToast } from "../../../util/toast";
|
|
||||||
import { indentStyle, overflowStyles } from "../automation/styles";
|
import { indentStyle, overflowStyles } from "../automation/styles";
|
||||||
import "./ha-script-field-selector-editor";
|
import "./ha-script-field-selector-editor";
|
||||||
import type HaScriptFieldSelectorEditor from "./ha-script-field-selector-editor";
|
import type HaScriptFieldSelectorEditor from "./ha-script-field-selector-editor";
|
||||||
|
import { showToast } from "../../../util/toast";
|
||||||
|
|
||||||
@customElement("ha-script-field-row")
|
@customElement("ha-script-field-row")
|
||||||
export default class HaScriptFieldRow extends LitElement {
|
export default class HaScriptFieldRow extends LitElement {
|
||||||
@@ -80,33 +79,36 @@ export default class HaScriptFieldRow extends LitElement {
|
|||||||
.highlight=${this.highlight}
|
.highlight=${this.highlight}
|
||||||
@delete-row=${this._onDelete}
|
@delete-row=${this._onDelete}
|
||||||
>
|
>
|
||||||
<ha-dropdown
|
<ha-md-button-menu
|
||||||
|
quick
|
||||||
slot="icons"
|
slot="icons"
|
||||||
@click=${preventDefaultStopPropagation}
|
@click=${preventDefaultStopPropagation}
|
||||||
@keydown=${stopPropagation}
|
@keydown=${stopPropagation}
|
||||||
@wa-select=${this._handleDropdownSelect}
|
@closed=${stopPropagation}
|
||||||
placement="bottom-end"
|
positioning="fixed"
|
||||||
|
anchor-corner="end-end"
|
||||||
|
menu-corner="start-end"
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-dropdown-item value="toggle_yaml_mode">
|
<ha-md-menu-item .clickAction=${this._toggleYamlMode}>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
|
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
|
||||||
)}
|
)}
|
||||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="delete"
|
.clickAction=${this._onDelete}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
variant="danger"
|
class="warning"
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiDelete}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiDelete}></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.delete"
|
"ui.panel.config.automation.editor.actions.delete"
|
||||||
@@ -116,6 +118,7 @@ export default class HaScriptFieldRow extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -131,8 +134,8 @@ export default class HaScriptFieldRow extends LitElement {
|
|||||||
</span>`
|
</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
</ha-dropdown>
|
</ha-md-button-menu>
|
||||||
|
|
||||||
<h3 slot="header">${this.key}</h3>
|
<h3 slot="header">${this.key}</h3>
|
||||||
|
|
||||||
@@ -167,21 +170,27 @@ export default class HaScriptFieldRow extends LitElement {
|
|||||||
"ui.panel.config.script.editor.field.selector"
|
"ui.panel.config.script.editor.field.selector"
|
||||||
)}
|
)}
|
||||||
</h3>
|
</h3>
|
||||||
<ha-dropdown
|
<ha-md-button-menu
|
||||||
|
quick
|
||||||
slot="icons"
|
slot="icons"
|
||||||
@click=${preventDefaultStopPropagation}
|
@click=${preventDefaultStopPropagation}
|
||||||
@keydown=${stopPropagation}
|
@keydown=${stopPropagation}
|
||||||
@wa-select=${this._handleDropdownSelect}
|
@closed=${stopPropagation}
|
||||||
placement="bottom-end"
|
positioning="fixed"
|
||||||
|
anchor-corner="end-end"
|
||||||
|
menu-corner="start-end"
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-dropdown-item value="toggle_yaml_mode" selector-row>
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._toggleYamlMode}
|
||||||
|
selector-row
|
||||||
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="start"
|
||||||
.path=${mdiPlaylistEdit}
|
.path=${mdiPlaylistEdit}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
@@ -192,13 +201,16 @@ export default class HaScriptFieldRow extends LitElement {
|
|||||||
class="shortcut-placeholder ${isMac ? "mac" : ""}"
|
class="shortcut-placeholder ${isMac ? "mac" : ""}"
|
||||||
></span>
|
></span>
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
<ha-dropdown-item
|
<ha-md-menu-item
|
||||||
value="delete"
|
.clickAction=${this._onDelete}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
variant="danger"
|
class="warning"
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiDelete}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
|
.path=${mdiDelete}
|
||||||
|
></ha-svg-icon>
|
||||||
<div class="overflow-label">
|
<div class="overflow-label">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.delete"
|
"ui.panel.config.automation.editor.actions.delete"
|
||||||
@@ -208,6 +220,7 @@ export default class HaScriptFieldRow extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${isMac
|
>${isMac
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
.path=${mdiAppleKeyboardCommand}
|
.path=${mdiAppleKeyboardCommand}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
@@ -223,8 +236,8 @@ export default class HaScriptFieldRow extends LitElement {
|
|||||||
</span>`
|
</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</ha-dropdown-item>
|
</ha-md-menu-item>
|
||||||
</ha-dropdown>
|
</ha-md-button-menu>
|
||||||
</ha-automation-row>
|
</ha-automation-row>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
${typeof this.field.selector === "object" &&
|
${typeof this.field.selector === "object" &&
|
||||||
@@ -407,23 +420,6 @@ export default class HaScriptFieldRow extends LitElement {
|
|||||||
this._selectorRowElement?.focus();
|
this._selectorRowElement?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "toggle_yaml_mode":
|
|
||||||
this._toggleYamlMode(ev.target as HTMLElement);
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
this._onDelete();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
@@ -481,6 +477,9 @@ export default class HaScriptFieldRow extends LitElement {
|
|||||||
.selected_menu_item {
|
.selected_menu_item {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
|
li[role="separator"] {
|
||||||
|
border-bottom-color: var(--divider-color);
|
||||||
|
}
|
||||||
.selector-row {
|
.selector-row {
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
|
||||||
import {
|
import {
|
||||||
mdiDotsVertical,
|
mdiDotsVertical,
|
||||||
mdiDownload,
|
mdiDownload,
|
||||||
@@ -9,19 +8,17 @@ import {
|
|||||||
mdiRefresh,
|
mdiRefresh,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { repeat } from "lit/directives/repeat";
|
import { repeat } from "lit/directives/repeat";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
import { formatDateTimeWithSeconds } from "../../../common/datetime/format_date_time";
|
import { formatDateTimeWithSeconds } from "../../../common/datetime/format_date_time";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { navigate } from "../../../common/navigate";
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
import "../../../components/ha-dropdown";
|
|
||||||
import "../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
|
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
|
import "../../../components/ha-list-item";
|
||||||
import "../../../components/trace/ha-trace-blueprint-config";
|
import "../../../components/trace/ha-trace-blueprint-config";
|
||||||
import "../../../components/trace/ha-trace-config";
|
import "../../../components/trace/ha-trace-config";
|
||||||
import "../../../components/trace/ha-trace-logbook";
|
import "../../../components/trace/ha-trace-logbook";
|
||||||
@@ -107,7 +104,7 @@ export class HaScriptTrace extends LitElement {
|
|||||||
? html`
|
? html`
|
||||||
<ha-button
|
<ha-button
|
||||||
class="trace-link"
|
class="trace-link"
|
||||||
@click=${this._navigateToScript}
|
href="/config/script/edit/${this.scriptId}"
|
||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
appearance="plain"
|
appearance="plain"
|
||||||
>
|
>
|
||||||
@@ -116,49 +113,64 @@ export class HaScriptTrace extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</ha-button>
|
</ha-button>
|
||||||
`
|
`
|
||||||
: nothing}
|
: ""}
|
||||||
|
|
||||||
<ha-dropdown
|
<ha-button-menu slot="toolbar-icon">
|
||||||
slot="toolbar-icon"
|
|
||||||
@wa-select=${this._handleDropdownSelect}
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
|
||||||
<ha-dropdown-item .disabled=${!stateObj} value="show_info">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${!stateObj}
|
||||||
|
@click=${this._showInfo}
|
||||||
|
>
|
||||||
${this.hass.localize("ui.panel.config.script.editor.show_info")}
|
${this.hass.localize("ui.panel.config.script.editor.show_info")}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiInformationOutline}
|
.path=${mdiInformationOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
${this.narrow && this.scriptId
|
${this.narrow && this.scriptId
|
||||||
? html`<ha-dropdown-item value="edit_script">
|
? html`
|
||||||
|
<a
|
||||||
|
class="trace-link"
|
||||||
|
href="/config/script/edit/${this.scriptId}"
|
||||||
|
>
|
||||||
|
<ha-list-item graphic="icon">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.script.trace.edit_script"
|
"ui.panel.config.script.trace.edit_script"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPencil}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
</ha-dropdown-item> `
|
slot="graphic"
|
||||||
: nothing}
|
.path=${mdiPencil}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-list-item>
|
||||||
|
</a>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
|
||||||
<wa-divider></wa-divider>
|
<li divider role="separator"></li>
|
||||||
|
|
||||||
<ha-dropdown-item value="refresh">
|
<ha-list-item graphic="icon" @click=${this._refreshTraces}>
|
||||||
${this.hass.localize("ui.panel.config.automation.trace.refresh")}
|
${this.hass.localize("ui.panel.config.automation.trace.refresh")}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiRefresh}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiRefresh}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
|
|
||||||
<ha-dropdown-item .disabled=${!this._trace} value="download_trace">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${!this._trace}
|
||||||
|
@click=${this._downloadTrace}
|
||||||
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.trace.download_trace"
|
"ui.panel.config.automation.trace.download_trace"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiDownload}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiDownload}></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
</ha-dropdown>
|
</ha-button-menu>
|
||||||
|
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
${this._traces && this._traces.length > 0
|
${this._traces && this._traces.length > 0
|
||||||
@@ -518,35 +530,6 @@ export class HaScriptTrace extends LitElement {
|
|||||||
fireEvent(this, "hass-more-info", { entityId: this._entityId });
|
fireEvent(this, "hass-more-info", { entityId: this._entityId });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _navigateToScript() {
|
|
||||||
if (this.scriptId) {
|
|
||||||
navigate(`/config/script/edit/${this.scriptId}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
|
||||||
const action = ev.detail?.item?.value;
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "show_info":
|
|
||||||
this._showInfo();
|
|
||||||
break;
|
|
||||||
case "refresh":
|
|
||||||
this._refreshTraces();
|
|
||||||
break;
|
|
||||||
case "download_trace":
|
|
||||||
this._downloadTrace();
|
|
||||||
break;
|
|
||||||
case "edit_script":
|
|
||||||
this._navigateToScript();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
|
|||||||
@@ -215,30 +215,25 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
? html`
|
? html`
|
||||||
<div class="messages">
|
<div class="messages">
|
||||||
${messages.map((content) =>
|
${messages.map((content) =>
|
||||||
content.role === "system"
|
content.role === "system" || content.role === "tool_result"
|
||||||
? content.content
|
|
||||||
? html`
|
|
||||||
<ha-expansion-panel
|
|
||||||
class="content-expansion ${content.role}"
|
|
||||||
>
|
|
||||||
<div slot="header">System</div>
|
|
||||||
<pre>${content.content}</pre>
|
|
||||||
</ha-expansion-panel>
|
|
||||||
`
|
|
||||||
: nothing
|
|
||||||
: content.role === "tool_result"
|
|
||||||
? html`
|
? html`
|
||||||
<ha-expansion-panel
|
<ha-expansion-panel
|
||||||
class="content-expansion ${content.role}"
|
class="content-expansion ${content.role}"
|
||||||
>
|
>
|
||||||
<div slot="header">
|
<div slot="header">
|
||||||
Result for ${content.tool_name}
|
${content.role === "system"
|
||||||
|
? "System"
|
||||||
|
: `Result for ${content.tool_name}`}
|
||||||
</div>
|
</div>
|
||||||
|
${content.role === "system"
|
||||||
|
? html`<pre>${content.content}</pre>`
|
||||||
|
: html`
|
||||||
<ha-yaml-editor
|
<ha-yaml-editor
|
||||||
read-only
|
read-only
|
||||||
auto-update
|
auto-update
|
||||||
.value=${content}
|
.value=${content}
|
||||||
></ha-yaml-editor>
|
></ha-yaml-editor>
|
||||||
|
`}
|
||||||
</ha-expansion-panel>
|
</ha-expansion-panel>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import type { CSSResultGroup, TemplateResult } from "lit";
|
|||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { navigate } from "../../common/navigate";
|
import { navigate } from "../../common/navigate";
|
||||||
import { withViewTransition } from "../../common/util/view-transition";
|
|
||||||
import "../../components/ha-button-menu";
|
import "../../components/ha-button-menu";
|
||||||
import "../../components/ha-icon-button";
|
import "../../components/ha-icon-button";
|
||||||
import "../../components/ha-list-item";
|
import "../../components/ha-list-item";
|
||||||
@@ -117,9 +116,7 @@ class PanelDeveloperTools extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (newPage !== this._page) {
|
if (newPage !== this._page) {
|
||||||
withViewTransition(() => {
|
|
||||||
navigate(`/developer-tools/${newPage}`);
|
navigate(`/developer-tools/${newPage}`);
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
scrollTo({ behavior: "smooth", top: 0 });
|
scrollTo({ behavior: "smooth", top: 0 });
|
||||||
}
|
}
|
||||||
@@ -128,9 +125,7 @@ class PanelDeveloperTools extends LitElement {
|
|||||||
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||||
switch (ev.detail.index) {
|
switch (ev.detail.index) {
|
||||||
case 0:
|
case 0:
|
||||||
withViewTransition(() => {
|
|
||||||
navigate(`/developer-tools/debug`);
|
navigate(`/developer-tools/debug`);
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -268,10 +268,8 @@ class PanelEnergy extends LitElement {
|
|||||||
(source) => source.type === "gas"
|
(source) => source.type === "gas"
|
||||||
);
|
);
|
||||||
|
|
||||||
const hasDeviceConsumption = this._prefs.device_consumption.length > 0;
|
|
||||||
|
|
||||||
const views: LovelaceViewConfig[] = [];
|
const views: LovelaceViewConfig[] = [];
|
||||||
if (hasEnergy || hasDeviceConsumption) {
|
if (hasEnergy) {
|
||||||
views.push(ENERGY_VIEW);
|
views.push(ENERGY_VIEW);
|
||||||
}
|
}
|
||||||
if (hasGas) {
|
if (hasGas) {
|
||||||
|
|||||||
@@ -8,6 +8,14 @@ import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strat
|
|||||||
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
|
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
|
||||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
|
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
|
||||||
|
|
||||||
|
const sourceHasCost = (source: Record<string, any>): boolean =>
|
||||||
|
Boolean(
|
||||||
|
source.stat_cost ||
|
||||||
|
source.stat_compensation ||
|
||||||
|
source.entity_energy_price ||
|
||||||
|
source.number_energy_price
|
||||||
|
);
|
||||||
|
|
||||||
@customElement("energy-overview-view-strategy")
|
@customElement("energy-overview-view-strategy")
|
||||||
export class EnergyOverviewViewStrategy extends ReactiveElement {
|
export class EnergyOverviewViewStrategy extends ReactiveElement {
|
||||||
static async generate(
|
static async generate(
|
||||||
@@ -27,9 +35,6 @@ export class EnergyOverviewViewStrategy extends ReactiveElement {
|
|||||||
const energyCollection = getEnergyDataCollection(hass, {
|
const energyCollection = getEnergyDataCollection(hass, {
|
||||||
key: collectionKey,
|
key: collectionKey,
|
||||||
});
|
});
|
||||||
if (!energyCollection.prefs) {
|
|
||||||
await energyCollection.refresh();
|
|
||||||
}
|
|
||||||
const prefs = energyCollection.prefs;
|
const prefs = energyCollection.prefs;
|
||||||
|
|
||||||
// No energy sources available
|
// No energy sources available
|
||||||
@@ -63,6 +68,13 @@ export class EnergyOverviewViewStrategy extends ReactiveElement {
|
|||||||
(source.type === "battery" && source.stat_rate) ||
|
(source.type === "battery" && source.stat_rate) ||
|
||||||
(source.type === "grid" && source.power?.length)
|
(source.type === "grid" && source.power?.length)
|
||||||
);
|
);
|
||||||
|
const hasCost = prefs.energy_sources.some(
|
||||||
|
(source) =>
|
||||||
|
sourceHasCost(source) ||
|
||||||
|
(source.type === "grid" &&
|
||||||
|
(source.flow_from?.some(sourceHasCost) ||
|
||||||
|
source.flow_to?.some(sourceHasCost)))
|
||||||
|
);
|
||||||
|
|
||||||
const overviewSection: LovelaceSectionConfig = {
|
const overviewSection: LovelaceSectionConfig = {
|
||||||
type: "grid",
|
type: "grid",
|
||||||
@@ -83,7 +95,7 @@ export class EnergyOverviewViewStrategy extends ReactiveElement {
|
|||||||
}
|
}
|
||||||
view.sections!.push(overviewSection);
|
view.sections!.push(overviewSection);
|
||||||
|
|
||||||
if (prefs.energy_sources.length) {
|
if (hasCost) {
|
||||||
view.sections!.push({
|
view.sections!.push({
|
||||||
type: "grid",
|
type: "grid",
|
||||||
cards: [
|
cards: [
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ export class EnergyViewStrategy extends ReactiveElement {
|
|||||||
const energyCollection = getEnergyDataCollection(hass, {
|
const energyCollection = getEnergyDataCollection(hass, {
|
||||||
key: collectionKey,
|
key: collectionKey,
|
||||||
});
|
});
|
||||||
if (!energyCollection.prefs) {
|
|
||||||
await energyCollection.refresh();
|
|
||||||
}
|
|
||||||
const prefs = energyCollection.prefs;
|
const prefs = energyCollection.prefs;
|
||||||
|
|
||||||
// No energy sources available
|
// No energy sources available
|
||||||
|
|||||||
@@ -24,9 +24,6 @@ export class GasViewStrategy extends ReactiveElement {
|
|||||||
const energyCollection = getEnergyDataCollection(hass, {
|
const energyCollection = getEnergyDataCollection(hass, {
|
||||||
key: collectionKey,
|
key: collectionKey,
|
||||||
});
|
});
|
||||||
if (!energyCollection.prefs) {
|
|
||||||
await energyCollection.refresh();
|
|
||||||
}
|
|
||||||
const prefs = energyCollection.prefs;
|
const prefs = energyCollection.prefs;
|
||||||
|
|
||||||
const hasGasSources = prefs?.energy_sources.some(
|
const hasGasSources = prefs?.energy_sources.some(
|
||||||
|
|||||||
@@ -24,9 +24,6 @@ export class WaterViewStrategy extends ReactiveElement {
|
|||||||
const energyCollection = getEnergyDataCollection(hass, {
|
const energyCollection = getEnergyDataCollection(hass, {
|
||||||
key: collectionKey,
|
key: collectionKey,
|
||||||
});
|
});
|
||||||
if (!energyCollection.prefs) {
|
|
||||||
await energyCollection.refresh();
|
|
||||||
}
|
|
||||||
const prefs = energyCollection.prefs;
|
const prefs = energyCollection.prefs;
|
||||||
|
|
||||||
const hasWaterSources = prefs?.energy_sources.some(
|
const hasWaterSources = prefs?.energy_sources.some(
|
||||||
|
|||||||
@@ -56,19 +56,6 @@ export function getSuggestedPeriod(
|
|||||||
return dayDifference > 35 ? "month" : dayDifference > 2 ? "day" : "hour";
|
return dayDifference > 35 ? "month" : dayDifference > 2 ? "day" : "hour";
|
||||||
}
|
}
|
||||||
|
|
||||||
function createYAxisLabelFormatter(locale: FrontendLocaleData) {
|
|
||||||
let previousValue: number | undefined;
|
|
||||||
|
|
||||||
return (value: number): string => {
|
|
||||||
const maximumFractionDigits = Math.max(
|
|
||||||
1,
|
|
||||||
-Math.floor(Math.log10(Math.abs(value - (previousValue ?? value) || 1)))
|
|
||||||
);
|
|
||||||
previousValue = value;
|
|
||||||
return formatNumber(value, locale, { maximumFractionDigits });
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCommonOptions(
|
export function getCommonOptions(
|
||||||
start: Date,
|
start: Date,
|
||||||
end: Date,
|
end: Date,
|
||||||
@@ -99,7 +86,7 @@ export function getCommonOptions(
|
|||||||
align: "left",
|
align: "left",
|
||||||
},
|
},
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
formatter: createYAxisLabelFormatter(locale),
|
formatter: (value: number) => formatNumber(Math.abs(value), locale),
|
||||||
},
|
},
|
||||||
splitLine: {
|
splitLine: {
|
||||||
show: true,
|
show: true,
|
||||||
|
|||||||
@@ -6,10 +6,7 @@ import { classMap } from "lit/directives/class-map";
|
|||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-svg-icon";
|
import "../../../../components/ha-svg-icon";
|
||||||
import type { EnergyData, EnergyPreferences } from "../../../../data/energy";
|
import type { EnergyData, EnergyPreferences } from "../../../../data/energy";
|
||||||
import {
|
import { getEnergyDataCollection } from "../../../../data/energy";
|
||||||
getEnergyDataCollection,
|
|
||||||
getPowerFromState,
|
|
||||||
} from "../../../../data/energy";
|
|
||||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import type { LovelaceCard, LovelaceGridOptions } from "../../types";
|
import type { LovelaceCard, LovelaceGridOptions } from "../../types";
|
||||||
@@ -727,7 +724,33 @@ class HuiPowerSankeyCard
|
|||||||
// Track this entity for state change detection
|
// Track this entity for state change detection
|
||||||
this._entities.add(entityId);
|
this._entities.add(entityId);
|
||||||
|
|
||||||
return getPowerFromState(this.hass.states[entityId]) ?? 0;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,10 +10,7 @@ import { LinearGradient } from "../../../../resources/echarts/echarts";
|
|||||||
import "../../../../components/chart/ha-chart-base";
|
import "../../../../components/chart/ha-chart-base";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import type { EnergyData } from "../../../../data/energy";
|
import type { EnergyData } from "../../../../data/energy";
|
||||||
import {
|
import { getEnergyDataCollection } from "../../../../data/energy";
|
||||||
getEnergyDataCollection,
|
|
||||||
getPowerFromState,
|
|
||||||
} from "../../../../data/energy";
|
|
||||||
import type { StatisticValue } from "../../../../data/recorder";
|
import type { StatisticValue } from "../../../../data/recorder";
|
||||||
import type { FrontendLocaleData } from "../../../../data/translation";
|
import type { FrontendLocaleData } from "../../../../data/translation";
|
||||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||||
@@ -200,7 +197,6 @@ export class HuiPowerSourcesGraphCard
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const now = Date.now();
|
|
||||||
Object.keys(statIds).forEach((key, keyIndex) => {
|
Object.keys(statIds).forEach((key, keyIndex) => {
|
||||||
if (statIds[key].stats.length) {
|
if (statIds[key].stats.length) {
|
||||||
const colorHex = computedStyles.getPropertyValue(statIds[key].color);
|
const colorHex = computedStyles.getPropertyValue(statIds[key].color);
|
||||||
@@ -208,14 +204,7 @@ export class HuiPowerSourcesGraphCard
|
|||||||
// Echarts is supposed to handle that but it is bugged when you use it together with stacking.
|
// 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
|
// The interpolation breaks the stacking, so this positive/negative is a workaround
|
||||||
const { positive, negative } = this._processData(
|
const { positive, negative } = this._processData(
|
||||||
statIds[key].stats.map((id: string) => {
|
statIds[key].stats.map((id: string) => energyData.stats[id] ?? [])
|
||||||
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({
|
datasets.push({
|
||||||
...commonSeriesOptions,
|
...commonSeriesOptions,
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard {
|
|||||||
throw new Error("Entities need to be an array");
|
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) {
|
if (this._config?.entities !== config.entities) {
|
||||||
this._fetchCalendarEvents();
|
this._fetchCalendarEvents();
|
||||||
}
|
}
|
||||||
@@ -87,20 +93,6 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard {
|
|||||||
this._config = { initial_view: "dayGridMonth", ...config };
|
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 {
|
public getCardSize(): number {
|
||||||
return 12;
|
return 12;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import type {
|
|||||||
import type {
|
import type {
|
||||||
LovelaceCard,
|
LovelaceCard,
|
||||||
LovelaceCardEditor,
|
LovelaceCardEditor,
|
||||||
LovelaceGridOptions,
|
|
||||||
LovelaceHeaderFooter,
|
LovelaceHeaderFooter,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
import type { EntitiesCardConfig } from "./types";
|
import type { EntitiesCardConfig } from "./types";
|
||||||
@@ -140,15 +139,6 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getGridOptions(): LovelaceGridOptions {
|
|
||||||
return {
|
|
||||||
columns: 12,
|
|
||||||
rows: "auto",
|
|
||||||
min_columns: 3,
|
|
||||||
fixed_rows: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public setConfig(config: EntitiesCardConfig): void {
|
public setConfig(config: EntitiesCardConfig): void {
|
||||||
if (!config.entities || !Array.isArray(config.entities)) {
|
if (!config.entities || !Array.isArray(config.entities)) {
|
||||||
throw new Error("Entities must be specified");
|
throw new Error("Entities must be specified");
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const entityState = Number(stateObj.state);
|
||||||
|
|
||||||
if (stateObj.state === UNAVAILABLE) {
|
if (stateObj.state === UNAVAILABLE) {
|
||||||
return html`
|
return html`
|
||||||
<hui-warning
|
<hui-warning
|
||||||
@@ -162,7 +164,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
.unit_of_measurement ||
|
.unit_of_measurement ||
|
||||||
""}
|
""}
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
"--gauge-color": this._computeSeverity(Number(valueToDisplay)),
|
"--gauge-color": this._computeSeverity(entityState),
|
||||||
})}
|
})}
|
||||||
.needle=${this._config!.needle}
|
.needle=${this._config!.needle}
|
||||||
.levels=${this._config!.needle ? this._severityLevels() : undefined}
|
.levels=${this._config!.needle ? this._severityLevels() : undefined}
|
||||||
|
|||||||
@@ -94,10 +94,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return locationEntities.filter(
|
return locationEntities.filter((entity) => !personSources.has(entity));
|
||||||
(entityId) =>
|
|
||||||
!hass.entities?.[entityId]?.hidden && !personSources.has(entityId)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: MapCardConfig): void {
|
public setConfig(config: MapCardConfig): void {
|
||||||
|
|||||||
@@ -3,11 +3,7 @@ import { property, state } from "lit/decorators";
|
|||||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||||
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import type {
|
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||||
LovelaceCard,
|
|
||||||
LovelaceCardEditor,
|
|
||||||
LovelaceGridOptions,
|
|
||||||
} from "../types";
|
|
||||||
import "./hui-card";
|
import "./hui-card";
|
||||||
import type { HuiCard } from "./hui-card";
|
import type { HuiCard } from "./hui-card";
|
||||||
import type { StackCardConfig } from "./types";
|
import type { StackCardConfig } from "./types";
|
||||||
@@ -43,15 +39,6 @@ export abstract class HuiStackCard<T extends StackCardConfig = StackCardConfig>
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getGridOptions(): LovelaceGridOptions {
|
|
||||||
return {
|
|
||||||
columns: 12,
|
|
||||||
rows: "auto",
|
|
||||||
min_columns: 3,
|
|
||||||
fixed_rows: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public setConfig(config: T): void {
|
public setConfig(config: T): void {
|
||||||
if (!config || !config.cards || !Array.isArray(config.cards)) {
|
if (!config || !config.cards || !Array.isArray(config.cards)) {
|
||||||
throw new Error("Invalid configuration");
|
throw new Error("Invalid configuration");
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { List } from "@material/mwc-list/mwc-list";
|
import type { List } from "@material/mwc-list/mwc-list";
|
||||||
|
import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||||
import {
|
import {
|
||||||
mdiClock,
|
mdiClock,
|
||||||
mdiDelete,
|
mdiDelete,
|
||||||
@@ -17,16 +18,15 @@ import { classMap } from "lit/directives/class-map";
|
|||||||
import { repeat } from "lit/directives/repeat";
|
import { repeat } from "lit/directives/repeat";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||||
|
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
|
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-check-list-item";
|
import "../../../components/ha-check-list-item";
|
||||||
import "../../../components/ha-checkbox";
|
import "../../../components/ha-checkbox";
|
||||||
import "../../../components/ha-dropdown";
|
|
||||||
import "../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
|
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-list";
|
import "../../../components/ha-list";
|
||||||
|
import "../../../components/ha-list-item";
|
||||||
import "../../../components/ha-markdown-element";
|
import "../../../components/ha-markdown-element";
|
||||||
import "../../../components/ha-relative-time";
|
import "../../../components/ha-relative-time";
|
||||||
import "../../../components/ha-select";
|
import "../../../components/ha-select";
|
||||||
@@ -378,29 +378,28 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
|
|||||||
${this._todoListSupportsFeature(
|
${this._todoListSupportsFeature(
|
||||||
TodoListEntityFeature.DELETE_TODO_ITEM
|
TodoListEntityFeature.DELETE_TODO_ITEM
|
||||||
)
|
)
|
||||||
? html`<ha-dropdown
|
? html`<ha-button-menu
|
||||||
@wa-select=${this._handleCompletedMenuSelect}
|
@closed=${stopPropagation}
|
||||||
placement="bottom-end"
|
fixed
|
||||||
|
@action=${this._handleCompletedMenuAction}
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-dropdown-item
|
<ha-list-item graphic="icon" class="warning">
|
||||||
value="clear"
|
|
||||||
variant="danger"
|
|
||||||
>
|
|
||||||
${this.hass!.localize(
|
${this.hass!.localize(
|
||||||
"ui.panel.lovelace.cards.todo-list.clear_items"
|
"ui.panel.lovelace.cards.todo-list.clear_items"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class="warning"
|
class="warning"
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiDeleteSweep}
|
.path=${mdiDeleteSweep}
|
||||||
|
.disabled=${unavailable}
|
||||||
>
|
>
|
||||||
</ha-svg-icon>
|
</ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
</ha-dropdown>`
|
</ha-button-menu>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>`
|
</div>`
|
||||||
: nothing}
|
: nothing}
|
||||||
@@ -414,27 +413,33 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderMenu(config: TodoListCardConfig, _unavailable: boolean) {
|
private _renderMenu(config: TodoListCardConfig, unavailable: boolean) {
|
||||||
return (!config.display_order ||
|
return (!config.display_order ||
|
||||||
config.display_order === TodoSortMode.NONE) &&
|
config.display_order === TodoSortMode.NONE) &&
|
||||||
this._todoListSupportsFeature(TodoListEntityFeature.MOVE_TODO_ITEM)
|
this._todoListSupportsFeature(TodoListEntityFeature.MOVE_TODO_ITEM)
|
||||||
? html`<ha-dropdown
|
? html`<ha-button-menu
|
||||||
@wa-select=${this._handlePrimaryMenuSelect}
|
@closed=${stopPropagation}
|
||||||
placement="bottom-end"
|
fixed
|
||||||
|
@action=${this._handlePrimaryMenuAction}
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-dropdown-item value="reorder">
|
<ha-list-item graphic="icon">
|
||||||
${this.hass!.localize(
|
${this.hass!.localize(
|
||||||
this._reordering
|
this._reordering
|
||||||
? "ui.panel.lovelace.cards.todo-list.exit_reorder_items"
|
? "ui.panel.lovelace.cards.todo-list.exit_reorder_items"
|
||||||
: "ui.panel.lovelace.cards.todo-list.reorder_items"
|
: "ui.panel.lovelace.cards.todo-list.reorder_items"
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="icon" .path=${mdiSort}> </ha-svg-icon>
|
<ha-svg-icon
|
||||||
</ha-dropdown-item>
|
slot="graphic"
|
||||||
</ha-dropdown>`
|
.path=${mdiSort}
|
||||||
|
.disabled=${unavailable}
|
||||||
|
>
|
||||||
|
</ha-svg-icon>
|
||||||
|
</ha-list-item>
|
||||||
|
</ha-button-menu>`
|
||||||
: nothing;
|
: nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,11 +641,11 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleCompletedMenuSelect(
|
private _handleCompletedMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||||
ev: CustomEvent<{ item: HaDropdownItem }>
|
switch (ev.detail.index) {
|
||||||
) {
|
case 0:
|
||||||
if (ev.detail?.item?.value === "clear") {
|
|
||||||
this._clearCompletedItems();
|
this._clearCompletedItems();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,9 +704,11 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handlePrimaryMenuSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
private _handlePrimaryMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||||
if (ev.detail?.item?.value === "reorder") {
|
switch (ev.detail.index) {
|
||||||
|
case 0:
|
||||||
this._toggleReorder();
|
this._toggleReorder();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,10 +23,8 @@ import {
|
|||||||
subscribeForecast,
|
subscribeForecast,
|
||||||
weatherAttrIcons,
|
weatherAttrIcons,
|
||||||
weatherSVGStyles,
|
weatherSVGStyles,
|
||||||
WEATHER_TEMPERATURE_ATTRIBUTES,
|
|
||||||
} from "../../../data/weather";
|
} from "../../../data/weather";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { round } from "../../../common/number/round";
|
|
||||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
|
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
|
||||||
import { findEntities } from "../common/find-entities";
|
import { findEntities } from "../common/find-entities";
|
||||||
@@ -268,20 +266,6 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
|
|||||||
this._config.name
|
this._config.name
|
||||||
);
|
);
|
||||||
|
|
||||||
const temperatureFractionDigits = this._config.round_temperature
|
|
||||||
? 0
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const isSecondaryInfoAttributeTemperature =
|
|
||||||
this._config?.secondary_info_attribute &&
|
|
||||||
WEATHER_TEMPERATURE_ATTRIBUTES.has(this._config.secondary_info_attribute);
|
|
||||||
|
|
||||||
const isSecondaryInfoNumber =
|
|
||||||
this._config.secondary_info_attribute &&
|
|
||||||
!Number.isNaN(
|
|
||||||
+stateObj.attributes[this._config.secondary_info_attribute]
|
|
||||||
);
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
<ha-card
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
@@ -328,11 +312,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
|
|||||||
? html`
|
? html`
|
||||||
${formatNumber(
|
${formatNumber(
|
||||||
stateObj.attributes.temperature,
|
stateObj.attributes.temperature,
|
||||||
this.hass.locale,
|
this.hass.locale
|
||||||
{
|
|
||||||
maximumFractionDigits:
|
|
||||||
temperatureFractionDigits,
|
|
||||||
}
|
|
||||||
)} <span
|
)} <span
|
||||||
>${getWeatherUnit(
|
>${getWeatherUnit(
|
||||||
this.hass.config,
|
this.hass.config,
|
||||||
@@ -370,26 +350,14 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
|
|||||||
: html`
|
: html`
|
||||||
${this.hass.formatEntityAttributeValue(
|
${this.hass.formatEntityAttributeValue(
|
||||||
stateObj,
|
stateObj,
|
||||||
this._config.secondary_info_attribute,
|
this._config.secondary_info_attribute
|
||||||
temperatureFractionDigits === 0 &&
|
|
||||||
isSecondaryInfoNumber &&
|
|
||||||
isSecondaryInfoAttributeTemperature
|
|
||||||
? round(
|
|
||||||
stateObj.attributes[
|
|
||||||
this._config
|
|
||||||
.secondary_info_attribute
|
|
||||||
],
|
|
||||||
temperatureFractionDigits
|
|
||||||
)
|
|
||||||
: undefined
|
|
||||||
)}
|
)}
|
||||||
`}
|
`}
|
||||||
`
|
`
|
||||||
: getSecondaryWeatherAttribute(
|
: getSecondaryWeatherAttribute(
|
||||||
this.hass,
|
this.hass,
|
||||||
stateObj,
|
stateObj,
|
||||||
forecast!,
|
forecast!
|
||||||
temperatureFractionDigits
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -457,11 +425,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
|
|||||||
${this._showValue(item.temperature)
|
${this._showValue(item.temperature)
|
||||||
? html`${formatNumber(
|
? html`${formatNumber(
|
||||||
item.temperature,
|
item.temperature,
|
||||||
this.hass!.locale,
|
this.hass!.locale
|
||||||
{
|
|
||||||
maximumFractionDigits:
|
|
||||||
temperatureFractionDigits,
|
|
||||||
}
|
|
||||||
)}°`
|
)}°`
|
||||||
: "—"}
|
: "—"}
|
||||||
</div>
|
</div>
|
||||||
@@ -469,11 +433,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
|
|||||||
${this._showValue(item.templow)
|
${this._showValue(item.templow)
|
||||||
? html`${formatNumber(
|
? html`${formatNumber(
|
||||||
item.templow!,
|
item.templow!,
|
||||||
this.hass!.locale,
|
this.hass!.locale
|
||||||
{
|
|
||||||
maximumFractionDigits:
|
|
||||||
temperatureFractionDigits,
|
|
||||||
}
|
|
||||||
)}°`
|
)}°`
|
||||||
: hourly
|
: hourly
|
||||||
? ""
|
? ""
|
||||||
|
|||||||
@@ -597,7 +597,6 @@ export interface WeatherForecastCardConfig extends LovelaceCardConfig {
|
|||||||
forecast_type?: ForecastType;
|
forecast_type?: ForecastType;
|
||||||
forecast_slots?: number;
|
forecast_slots?: number;
|
||||||
secondary_info_attribute?: keyof TranslationDict["ui"]["card"]["weather"]["attributes"];
|
secondary_info_attribute?: keyof TranslationDict["ui"]["card"]["weather"]["attributes"];
|
||||||
round_temperature?: boolean;
|
|
||||||
theme?: string;
|
theme?: string;
|
||||||
tap_action?: ActionConfig;
|
tap_action?: ActionConfig;
|
||||||
hold_action?: ActionConfig;
|
hold_action?: ActionConfig;
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ export const computeLovelaceEntityName = (
|
|||||||
if (!config) {
|
if (!config) {
|
||||||
return stateObj ? computeStateName(stateObj) : "";
|
return stateObj ? computeStateName(stateObj) : "";
|
||||||
}
|
}
|
||||||
if (typeof config !== "object") {
|
if (typeof config === "string") {
|
||||||
return String(config);
|
return config;
|
||||||
}
|
}
|
||||||
if (stateObj) {
|
if (stateObj) {
|
||||||
return hass.formatEntityName(stateObj, config);
|
return hass.formatEntityName(stateObj, config);
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import { computeStateDomain } from "../../../common/entity/compute_state_domain"
|
|||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import { splitByGroups } from "../../../common/entity/split_by_groups";
|
import { splitByGroups } from "../../../common/entity/split_by_groups";
|
||||||
import { stripPrefixFromEntityName } from "../../../common/entity/strip_prefix_from_entity_name";
|
import { stripPrefixFromEntityName } from "../../../common/entity/strip_prefix_from_entity_name";
|
||||||
import { orderCompare, stringCompare } from "../../../common/string/compare";
|
import { stringCompare } from "../../../common/string/compare";
|
||||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||||
import type { AreasDisplayValue } from "../../../components/ha-areas-display-editor";
|
import type { AreasDisplayValue } from "../../../components/ha-areas-display-editor";
|
||||||
|
import { areaCompare } from "../../../data/area_registry";
|
||||||
import type {
|
import type {
|
||||||
EnergyPreferences,
|
EnergyPreferences,
|
||||||
GridSourceTypeEnergyPreference,
|
GridSourceTypeEnergyPreference,
|
||||||
@@ -571,21 +572,13 @@ export const generateDefaultViewConfig = (
|
|||||||
|
|
||||||
const areaCards: LovelaceCardConfig[] = [];
|
const areaCards: LovelaceCardConfig[] = [];
|
||||||
|
|
||||||
const areaIds = Object.keys(areaEntries);
|
const sortedAreas = Object.keys(splittedByAreaDevice.areasWithEntities).sort(
|
||||||
|
areaCompare(areaEntries, areasPrefs?.order)
|
||||||
|
);
|
||||||
|
|
||||||
if (areasPrefs?.order) {
|
for (const areaId of sortedAreas) {
|
||||||
const areaOrder = areasPrefs.order;
|
|
||||||
areaIds.sort(orderCompare(areaOrder));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const areaId of areaIds) {
|
|
||||||
// Skip areas with no entities
|
|
||||||
if (!(areaId in splittedByAreaDevice.areasWithEntities)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const areaEntities = splittedByAreaDevice.areasWithEntities[areaId];
|
const areaEntities = splittedByAreaDevice.areasWithEntities[areaId];
|
||||||
const area = areaEntries[areaId];
|
const area = areaEntries[areaId];
|
||||||
|
|
||||||
areaCards.push(
|
areaCards.push(
|
||||||
...computeCards(
|
...computeCards(
|
||||||
hass,
|
hass,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
|
||||||
import {
|
import {
|
||||||
mdiContentCopy,
|
mdiContentCopy,
|
||||||
mdiContentCut,
|
mdiContentCut,
|
||||||
@@ -13,10 +12,9 @@ import { LitElement, css, html, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import "../../../components/ha-dropdown";
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
|
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
|
import "../../../components/ha-list-item";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
@@ -43,6 +41,9 @@ export class HuiCardEditMode extends LitElement {
|
|||||||
@property({ type: Boolean, attribute: "no-move" })
|
@property({ type: Boolean, attribute: "no-move" })
|
||||||
public noMove = false;
|
public noMove = false;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
public _menuOpened = false;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
public _hover = false;
|
public _hover = false;
|
||||||
|
|
||||||
@@ -90,7 +91,8 @@ export class HuiCardEditMode extends LitElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const showOverlay = (this._hover || this._focused) && !this.hiddenOverlay;
|
const showOverlay =
|
||||||
|
(this._hover || this._menuOpened || this._focused) && !this.hiddenOverlay;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="card-wrapper" inert><slot></slot></div>
|
<div class="card-wrapper" inert><slot></slot></div>
|
||||||
@@ -113,71 +115,107 @@ export class HuiCardEditMode extends LitElement {
|
|||||||
<ha-svg-icon .path=${mdiPencil}> </ha-svg-icon>
|
<ha-svg-icon .path=${mdiPencil}> </ha-svg-icon>
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
<ha-dropdown
|
<ha-button-menu
|
||||||
class="more"
|
class="more"
|
||||||
placement="bottom-end"
|
corner="BOTTOM_END"
|
||||||
@wa-select=${this._handleDropdownSelect}
|
menu-corner="END"
|
||||||
|
.path=${[this.path!]}
|
||||||
|
@action=${this._handleAction}
|
||||||
|
@opened=${this._handleOpened}
|
||||||
|
@closed=${this._handleClosed}
|
||||||
>
|
>
|
||||||
<ha-icon-button slot="trigger" .path=${mdiDotsVertical}>
|
<ha-icon-button slot="trigger" .path=${mdiDotsVertical}>
|
||||||
</ha-icon-button>
|
</ha-icon-button>
|
||||||
${this.noEdit
|
${this.noEdit
|
||||||
? nothing
|
? nothing
|
||||||
: html`
|
: html`
|
||||||
<ha-dropdown-item value="edit">
|
<ha-list-item
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPencil}></ha-svg-icon>
|
graphic="icon"
|
||||||
|
@click=${this._handleAction}
|
||||||
|
.action=${"edit"}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="graphic" .path=${mdiPencil}></ha-svg-icon>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.edit_card.edit"
|
"ui.panel.lovelace.editor.edit_card.edit"
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
`}
|
`}
|
||||||
${this.noDuplicate
|
${this.noDuplicate
|
||||||
? nothing
|
? nothing
|
||||||
: html`
|
: html`
|
||||||
<ha-dropdown-item value="duplicate">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
@click=${this._handleAction}
|
||||||
|
.action=${"duplicate"}
|
||||||
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiPlusCircleMultipleOutline}
|
.path=${mdiPlusCircleMultipleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.edit_card.duplicate"
|
"ui.panel.lovelace.editor.edit_card.duplicate"
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
`}
|
`}
|
||||||
${this.noMove
|
${this.noMove
|
||||||
? nothing
|
? nothing
|
||||||
: html`
|
: html`
|
||||||
<ha-dropdown-item value="copy">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
@click=${this._handleAction}
|
||||||
|
.action=${"copy"}
|
||||||
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiContentCopy}
|
.path=${mdiContentCopy}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.edit_card.copy"
|
"ui.panel.lovelace.editor.edit_card.copy"
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
<ha-dropdown-item value="cut">
|
<ha-list-item
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCut}></ha-svg-icon>
|
graphic="icon"
|
||||||
|
@click=${this._handleAction}
|
||||||
|
.action=${"cut"}
|
||||||
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="graphic"
|
||||||
|
.path=${mdiContentCut}
|
||||||
|
></ha-svg-icon>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.edit_card.cut"
|
"ui.panel.lovelace.editor.edit_card.cut"
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
`}
|
`}
|
||||||
${this.noDuplicate && this.noEdit && this.noMove
|
${this.noDuplicate && this.noEdit && this.noMove
|
||||||
? nothing
|
? nothing
|
||||||
: html`<wa-divider></wa-divider>`}
|
: html`<li divider role="separator"></li>`}
|
||||||
<ha-dropdown-item value="delete" variant="danger">
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
class="warning"
|
||||||
|
@click=${this._handleAction}
|
||||||
|
.action=${"delete"}
|
||||||
|
>
|
||||||
${this.hass.localize("ui.panel.lovelace.editor.edit_card.delete")}
|
${this.hass.localize("ui.panel.lovelace.editor.edit_card.delete")}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class="warning"
|
class="warning"
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiDelete}
|
.path=${mdiDelete}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
</ha-dropdown>
|
</ha-button-menu>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleOpened() {
|
||||||
|
this._menuOpened = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleClosed() {
|
||||||
|
this._menuOpened = false;
|
||||||
|
}
|
||||||
|
|
||||||
private _handleOverlayClick(ev): void {
|
private _handleOverlayClick(ev): void {
|
||||||
if (ev.defaultPrevented) {
|
if (ev.defaultPrevented) {
|
||||||
return;
|
return;
|
||||||
@@ -190,14 +228,8 @@ export class HuiCardEditMode extends LitElement {
|
|||||||
this._editCard();
|
this._editCard();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
private _handleAction(ev) {
|
||||||
const action = ev.detail?.item?.value;
|
switch (ev.currentTarget.action) {
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "edit":
|
case "edit":
|
||||||
this._editCard();
|
this._editCard();
|
||||||
break;
|
break;
|
||||||
@@ -298,12 +330,14 @@ export class HuiCardEditMode extends LitElement {
|
|||||||
background: var(--secondary-background-color);
|
background: var(--secondary-background-color);
|
||||||
--mdc-icon-size: 20px;
|
--mdc-icon-size: 20px;
|
||||||
}
|
}
|
||||||
.more ha-icon-button {
|
.more {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -6px;
|
right: -6px;
|
||||||
top: -6px;
|
top: -6px;
|
||||||
inset-inline-end: -6px;
|
inset-inline-end: -6px;
|
||||||
inset-inline-start: initial;
|
inset-inline-start: initial;
|
||||||
|
}
|
||||||
|
.more ha-icon-button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: var(--ha-border-radius-circle);
|
border-radius: var(--ha-border-radius-circle);
|
||||||
background: var(--secondary-background-color);
|
background: var(--secondary-background-color);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||||
import {
|
import {
|
||||||
mdiContentCopy,
|
mdiContentCopy,
|
||||||
mdiContentCut,
|
mdiContentCut,
|
||||||
@@ -15,11 +15,10 @@ import { customElement, property, queryAssignedElements } from "lit/decorators";
|
|||||||
import { storage } from "../../../common/decorators/storage";
|
import { storage } from "../../../common/decorators/storage";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-dropdown";
|
|
||||||
import "../../../components/ha-dropdown-item";
|
|
||||||
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
|
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
|
import "../../../components/ha-list-item";
|
||||||
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
||||||
import { saveConfig } from "../../../data/lovelace/config/types";
|
import { saveConfig } from "../../../data/lovelace/config/types";
|
||||||
import { isStrategyView } from "../../../data/lovelace/config/view";
|
import { isStrategyView } from "../../../data/lovelace/config/view";
|
||||||
@@ -133,10 +132,7 @@ export class HuiCardOptions extends LitElement {
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
<ha-dropdown
|
<ha-button-menu @action=${this._handleAction}>
|
||||||
@wa-select=${this._handleDropdownSelect}
|
|
||||||
placement="bottom-end"
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass!.localize(
|
.label=${this.hass!.localize(
|
||||||
@@ -144,46 +140,52 @@ export class HuiCardOptions extends LitElement {
|
|||||||
)}
|
)}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-dropdown-item value="move">
|
<ha-list-item graphic="icon">
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiFileMoveOutline}
|
.path=${mdiFileMoveOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this.hass!.localize(
|
${this.hass!.localize(
|
||||||
"ui.panel.lovelace.editor.edit_card.move"
|
"ui.panel.lovelace.editor.edit_card.move"
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
<ha-dropdown-item value="duplicate">
|
<ha-list-item graphic="icon">
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiPlusCircleMultipleOutline}
|
.path=${mdiPlusCircleMultipleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this.hass!.localize(
|
${this.hass!.localize(
|
||||||
"ui.panel.lovelace.editor.edit_card.duplicate"
|
"ui.panel.lovelace.editor.edit_card.duplicate"
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
<ha-dropdown-item value="copy">
|
<ha-list-item graphic="icon">
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCopy}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
|
slot="graphic"
|
||||||
|
.path=${mdiContentCopy}
|
||||||
|
></ha-svg-icon>
|
||||||
${this.hass!.localize(
|
${this.hass!.localize(
|
||||||
"ui.panel.lovelace.editor.edit_card.copy"
|
"ui.panel.lovelace.editor.edit_card.copy"
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
<ha-dropdown-item value="cut">
|
<ha-list-item graphic="icon">
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentCut}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
|
slot="graphic"
|
||||||
|
.path=${mdiContentCut}
|
||||||
|
></ha-svg-icon>
|
||||||
${this.hass!.localize("ui.panel.lovelace.editor.edit_card.cut")}
|
${this.hass!.localize("ui.panel.lovelace.editor.edit_card.cut")}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
<wa-divider></wa-divider>
|
<li divider role="separator"></li>
|
||||||
<ha-dropdown-item value="delete" variant="danger">
|
<ha-list-item class="warning" graphic="icon">
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class="warning"
|
class="warning"
|
||||||
slot="icon"
|
slot="graphic"
|
||||||
.path=${mdiDelete}
|
.path=${mdiDelete}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this.hass!.localize(
|
${this.hass!.localize(
|
||||||
"ui.panel.lovelace.editor.edit_card.delete"
|
"ui.panel.lovelace.editor.edit_card.delete"
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
</ha-dropdown>
|
</ha-button-menu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
@@ -242,31 +244,30 @@ export class HuiCardOptions extends LitElement {
|
|||||||
ha-icon-button.move-arrow[disabled] {
|
ha-icon-button.move-arrow[disabled] {
|
||||||
color: var(--disabled-text-color);
|
color: var(--disabled-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ha-list-item {
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
private _handleAction(ev: CustomEvent<ActionDetail>) {
|
||||||
const action = ev.detail?.item?.value;
|
switch (ev.detail.index) {
|
||||||
|
case 0:
|
||||||
if (!action) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "move":
|
|
||||||
this._moveCard();
|
this._moveCard();
|
||||||
break;
|
break;
|
||||||
case "duplicate":
|
case 1:
|
||||||
this._duplicateCard();
|
this._duplicateCard();
|
||||||
break;
|
break;
|
||||||
case "copy":
|
case 2:
|
||||||
this._copyCard();
|
this._copyCard();
|
||||||
break;
|
break;
|
||||||
case "cut":
|
case 3:
|
||||||
this._cutCard();
|
this._cutCard();
|
||||||
break;
|
break;
|
||||||
case "delete":
|
case 4:
|
||||||
this._deleteCard();
|
this._deleteCard();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user