Compare commits

..

1 Commits

Author SHA1 Message Date
Paul Bottein
637eb6e894 Fix new script/automation dialog 2024-12-11 09:22:47 +01:00
1039 changed files with 23907 additions and 45151 deletions

View File

@@ -1,4 +1,5 @@
FROM mcr.microsoft.com/devcontainers/python:1-3.13
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.148.1/containers/python-3/.devcontainer/base.Dockerfile
FROM mcr.microsoft.com/devcontainers/python:3.12
ENV \
DEBIAN_FRONTEND=noninteractive \

View File

@@ -5,15 +5,12 @@
"context": ".."
},
"appPort": "8124:8123",
"postCreateCommand": "./.devcontainer/post_create.sh",
"postCreateCommand": "sudo apt update && sudo apt upgrade -y && sudo apt install -y libpcap-dev",
"postStartCommand": "script/bootstrap",
"containerEnv": {
"DEV_CONTAINER": "1",
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
},
"remoteEnv": {
"NODE_OPTIONS": "--max_old_space_size=8192"
},
"customizations": {
"vscode": {
"extensions": [

View File

@@ -1,22 +0,0 @@
#!/bin/bash
# This script will run after the container is created
# add github cli
(type -p wget >/dev/null || (sudo apt update && sudo apt-get install wget -y)) \
&& sudo mkdir -p -m 755 /etc/apt/keyrings \
&& out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \
&& cat $out | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \
&& sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
# Update package lists
sudo apt-get update
sudo apt upgrade -y
# Install necessary packages
sudo apt-get install -y libpcap-dev gh
# Display a message
echo "Post-create script has been executed successfully."

View File

@@ -7,7 +7,7 @@ body:
value: |
Make sure you are running the [latest version of Home Assistant][releases] before reporting an issue.
If you have a feature or enhancement request for the frontend, please [start a discussion][fr] instead of creating an issue.
If you have a feature or enhancement request for the frontend, please [start an discussion][fr] instead of creating an issue.
**Please do not report issues for custom cards.**
@@ -74,7 +74,7 @@ body:
If known, otherwise leave blank.
- type: input
attributes:
label: In which browser are you experiencing the issue?
label: In which browser are you experiencing the issue with?
placeholder: Google Chrome 88.0.4324.150
description: >
Provide the full name and don't forget to add the version!

View File

@@ -2,7 +2,7 @@ blank_issues_enabled: false
contact_links:
- name: Request a feature for the UI / Dashboards
url: https://github.com/home-assistant/frontend/discussions/category_choices
about: Request a new feature for the Home Assistant frontend.
about: Request an new feature for the Home Assistant frontend.
- name: Report a bug that is NOT related to the UI / Dashboards
url: https://github.com/home-assistant/core/issues
about: This is the issue tracker for our frontend. Please report other issues in the backend ("core") repository.

View File

@@ -26,7 +26,7 @@ jobs:
ref: dev
- name: Setup Node
uses: actions/setup-node@v4.2.0
uses: actions/setup-node@v4.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -62,7 +62,7 @@ jobs:
ref: master
- name: Setup Node
uses: actions/setup-node@v4.2.0
uses: actions/setup-node@v4.1.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -26,18 +26,24 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.2.0
id: setup-node
uses: actions/setup-node@v4.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
- uses: actions/cache@v4.2.0
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: "node_modules"
key: ${{ runner.os }}-yarn-${{ hashFiles('.yarnrc.yml') }}-${{ steps.setup-node.outputs.node-version }}-${{ hashFiles('yarn.lock') }}
restore-keys: ${{ runner.os }}-yarn-${{ hashFiles('.yarnrc.yml') }}-${{ steps.setup-node.outputs.node-version }}
- name: Install dependencies
if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn install --immutable
- name: Check for duplicate dependencies
run: yarn dedupe --check
- name: Build resources
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages
- name: Setup lint cache
uses: actions/cache@v4.2.1
uses: actions/cache@v4.2.0
with:
path: |
node_modules/.cache/prettier
@@ -60,11 +66,19 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.2.0
id: setup-node
uses: actions/setup-node@v4.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
- uses: actions/cache@v4.2.0
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: "node_modules"
key: ${{ runner.os }}-yarn-${{ hashFiles('.yarnrc.yml') }}-${{ steps.setup-node.outputs.node-version }}-${{ hashFiles('yarn.lock') }}
restore-keys: ${{ runner.os }}-yarn-${{ hashFiles('.yarnrc.yml') }}-${{ steps.setup-node.outputs.node-version }}
- name: Install dependencies
if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn install --immutable
- name: Build resources
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data
@@ -78,18 +92,26 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.2.0
id: setup-node
uses: actions/setup-node@v4.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
- uses: actions/cache@v4.2.0
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: "node_modules"
key: ${{ runner.os }}-yarn-${{ hashFiles('.yarnrc.yml') }}-${{ steps.setup-node.outputs.node-version }}-${{ hashFiles('yarn.lock') }}
restore-keys: ${{ runner.os }}-yarn-${{ hashFiles('.yarnrc.yml') }}-${{ steps.setup-node.outputs.node-version }}
- name: Install dependencies
if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn install --immutable
- name: Build Application
run: ./node_modules/.bin/gulp build-app
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@v4.6.1
uses: actions/upload-artifact@v4.4.3
with:
name: frontend-bundle-stats
path: build/stats/*.json
@@ -102,18 +124,26 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.2.0
id: setup-node
uses: actions/setup-node@v4.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
- uses: actions/cache@v4.2.0
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: "node_modules"
key: ${{ runner.os }}-yarn-${{ hashFiles('.yarnrc.yml') }}-${{ steps.setup-node.outputs.node-version }}-${{ hashFiles('yarn.lock') }}
restore-keys: ${{ runner.os }}-yarn-${{ hashFiles('.yarnrc.yml') }}-${{ steps.setup-node.outputs.node-version }}
- name: Install dependencies
if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn install --immutable
- name: Build Application
run: ./node_modules/.bin/gulp build-hassio
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@v4.6.1
uses: actions/upload-artifact@v4.4.3
with:
name: supervisor-bundle-stats
path: build/stats/*.json

View File

@@ -27,7 +27,7 @@ jobs:
ref: dev
- name: Setup Node
uses: actions/setup-node@v4.2.0
uses: actions/setup-node@v4.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -63,7 +63,7 @@ jobs:
ref: master
- name: Setup Node
uses: actions/setup-node@v4.2.0
uses: actions/setup-node@v4.1.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -19,7 +19,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.2.0
uses: actions/setup-node@v4.1.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -24,7 +24,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.2.0
uses: actions/setup-node@v4.1.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -6,7 +6,7 @@ on:
- cron: "0 1 * * *"
env:
PYTHON_VERSION: "3.13"
PYTHON_VERSION: "3.12"
NODE_OPTIONS: --max_old_space_size=6144
permissions:
@@ -28,7 +28,7 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node
uses: actions/setup-node@v4.2.0
uses: actions/setup-node@v4.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -57,14 +57,14 @@ jobs:
run: tar -czvf translations.tar.gz translations
- name: Upload build artifacts
uses: actions/upload-artifact@v4.6.1
uses: actions/upload-artifact@v4.4.3
with:
name: wheels
path: dist/home_assistant_frontend*.whl
if-no-files-found: error
- name: Upload translations
uses: actions/upload-artifact@v4.6.1
uses: actions/upload-artifact@v4.4.3
with:
name: translations
path: translations.tar.gz

View File

@@ -18,6 +18,6 @@ jobs:
pull-requests: read
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v6.1.0
- uses: release-drafter/release-drafter@v6.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -6,7 +6,7 @@ on:
- published
env:
PYTHON_VERSION: "3.13"
PYTHON_VERSION: "3.12"
NODE_OPTIONS: --max_old_space_size=6144
# Set default workflow permissions
@@ -25,16 +25,16 @@ jobs:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
- name: Verify version
uses: home-assistant/actions/helpers/verify-version@master
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Verify version
uses: home-assistant/actions/helpers/verify-version@master
- name: Setup Node
uses: actions/setup-node@v4.2.0
uses: actions/setup-node@v4.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -55,7 +55,7 @@ jobs:
script/release
- name: Upload release assets
uses: softprops/action-gh-release@v2.2.1
uses: softprops/action-gh-release@v2.1.0
with:
files: |
dist/*.whl
@@ -76,7 +76,7 @@ jobs:
- name: Build wheels
uses: home-assistant/wheels@2024.11.0
with:
abi: cp313
abi: cp312
tag: musllinux_1_2
arch: amd64
wheels-key: ${{ secrets.WHEELS_KEY }}
@@ -92,7 +92,7 @@ jobs:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.2.0
uses: actions/setup-node@v4.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -107,7 +107,7 @@ jobs:
- name: Tar folder
run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist .
- name: Upload release asset
uses: softprops/action-gh-release@v2.2.1
uses: softprops/action-gh-release@v2.1.0
with:
files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz
@@ -121,7 +121,7 @@ jobs:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.2.0
uses: actions/setup-node@v4.1.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -136,6 +136,6 @@ jobs:
- name: Tar folder
run: tar -czf hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz -C hassio/build .
- name: Upload release asset
uses: softprops/action-gh-release@v2.2.1
uses: softprops/action-gh-release@v2.1.0
with:
files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz

View File

@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 90 days stale policy
uses: actions/stale@v9.1.0
uses: actions/stale@v9.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 90

42
.vscode/tasks.json vendored
View File

@@ -1,42 +1,6 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Develop and serve Frontend",
"type": "shell",
"command": "script/develop_and_serve -c ${input:coreUrl}",
// Sync changes here to other tasks until issue resolved
// https://github.com/Microsoft/vscode/issues/61497
"problemMatcher": {
"owner": "ha-build",
"source": "ha-build",
"fileLocation": "absolute",
"severity": "error",
"pattern": [
{
"regexp": "(SyntaxError): (.+): (.+) \\((\\d+):(\\d+)\\)",
"severity": 1,
"file": 2,
"message": 3,
"line": 4,
"column": 5
}
],
"background": {
"activeOnStart": true,
"beginsPattern": "Changes detected. Starting compilation",
"endsPattern": "Build done @"
}
},
"isBackground": true,
"group": {
"kind": "build",
"isDefault": true
},
"runOptions": {
"instanceLimit": 1
}
},
{
"label": "Develop Frontend",
"type": "gulp",
@@ -277,12 +241,6 @@
"id": "supervisorToken",
"type": "promptString",
"description": "The token for the Remote API proxy add-on"
},
{
"id": "coreUrl",
"type": "promptString",
"description": "The URL of the Home Assistant Core instance",
"default": "http://127.0.0.1:8123"
}
]
}

File diff suppressed because one or more lines are too long

View File

@@ -6,4 +6,4 @@ enableGlobalCache: false
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.6.0.cjs
yarnPath: .yarn/releases/yarn-4.5.3.cjs

View File

@@ -5,7 +5,7 @@ import paths from "../paths.cjs";
const POLYFILL_DIR = join(paths.polymer_dir, "src/resources/polyfills");
// List of polyfill keys with supported browser targets for the functionality
const polyfillSupport = {
const PolyfillSupport = {
// Note states and shadowRoot properties should be supported.
"element-internals": {
android: 90,
@@ -18,6 +18,17 @@ const polyfillSupport = {
safari: 17.4,
samsung: 15.0,
},
"element-append": {
android: 54,
chrome: 54,
edge: 17,
firefox: 49,
ios: 10.0,
opera: 41,
opera_mobile: 41,
safari: 10.0,
samsung: 6.0,
},
"element-getattributenames": {
android: 61,
chrome: 61,
@@ -40,18 +51,27 @@ const polyfillSupport = {
safari: 12.0,
samsung: 10.0,
},
// FormatJS polyfill detects fix for https://bugs.chromium.org/p/v8/issues/detail?id=10682,
// so adjusted to several months after that was marked fixed
fetch: {
android: 42,
chrome: 42,
edge: 14,
firefox: 39,
ios: 10.3,
opera: 29,
opera_mobile: 29,
safari: 10.1,
samsung: 4.0,
},
"intl-getcanonicallocales": {
android: 90,
chrome: 90,
edge: 90,
android: 54,
chrome: 54,
edge: 16,
firefox: 48,
ios: 10.3,
opera: 76,
opera_mobile: 64,
opera: 41,
opera_mobile: 41,
safari: 10.1,
samsung: 15.0,
samsung: 6.0,
},
"intl-locale": {
android: 74,
@@ -67,6 +87,17 @@ const polyfillSupport = {
"intl-other": {
// Not specified (i.e. always try polyfill) since compatibility depends on supported locales
},
proxy: {
android: 49,
chrome: 49,
edge: 12,
firefox: 18,
ios: 10.0,
opera: 36,
opera_mobile: 36,
safari: 10.0,
samsung: 5.0,
},
"resize-observer": {
android: 64,
chrome: 64,
@@ -84,6 +115,8 @@ const polyfillSupport = {
// corresponding polyfill key and actual module to import
const polyfillMap = {
global: {
fetch: { key: "fetch", module: "unfetch/polyfill" },
Proxy: { key: "proxy", module: "proxy-polyfill" },
ResizeObserver: {
key: "resize-observer",
module: join(POLYFILL_DIR, "resize-observer.ts"),
@@ -95,7 +128,7 @@ const polyfillMap = {
module: "element-internals-polyfill",
},
...Object.fromEntries(
["getAttributeNames", "toggleAttribute"].map((prop) => {
["append", "getAttributeNames", "toggleAttribute"].map((prop) => {
const key = `element-${prop.toLowerCase()}`;
return [prop, { key, module: join(POLYFILL_DIR, `${key}.ts`) }];
})
@@ -135,7 +168,7 @@ export default defineProvider(
const resolvePolyfill = createMetaResolver(polyfillMap);
return {
name: "custom-polyfill",
polyfills: polyfillSupport,
polyfills: PolyfillSupport,
usageGlobal(meta, utils) {
const polyfill = resolvePolyfill(meta);
if (polyfill && shouldInjectPolyfill(polyfill.desc.key)) {

View File

@@ -1,16 +1,16 @@
// @ts-check
import tseslint from "typescript-eslint";
import rootConfig from "../eslint.config.mjs";
export default tseslint.config(...rootConfig, {
rules: {
"no-console": "off",
"import/no-extraneous-dependencies": "off",
"import/extensions": "off",
"import/no-dynamic-require": "off",
"global-require": "off",
"@typescript-eslint/no-require-imports": "off",
"prefer-arrow-callback": "off",
export default [
...rootConfig,
{
rules: {
"no-console": "off",
"import/no-extraneous-dependencies": "off",
"import/extensions": "off",
"import/no-dynamic-require": "off",
"global-require": "off",
"@typescript-eslint/no-var-requires": "off",
"prefer-arrow-callback": "off",
},
},
});
];

View File

@@ -3,7 +3,6 @@
import { constants } from "node:zlib";
import gulp from "gulp";
import brotli from "gulp-brotli";
import zopfli from "gulp-zopfli-green";
import paths from "../paths.cjs";
const filesGlob = "*.{js,json,css,svg,xml}";
@@ -13,74 +12,29 @@ const brotliOptions = {
[constants.BROTLI_PARAM_QUALITY]: constants.BROTLI_MAX_QUALITY,
},
};
const zopfliOptions = { threshold: 150 };
const compressModern = (rootDir, modernDir, compress) =>
gulp
.src([`${modernDir}/**/${filesGlob}`, `${rootDir}/sw-modern.js`], {
base: rootDir,
allowEmpty: true,
})
.pipe(compress === "zopfli" ? zopfli(zopfliOptions) : brotli(brotliOptions))
.pipe(gulp.dest(rootDir));
const compressOther = (rootDir, modernDir, compress) =>
const compressDistBrotli = (rootDir, modernDir, compressServiceWorker = true) =>
gulp
.src(
[
`${rootDir}/**/${filesGlob}`,
`!${modernDir}/**/${filesGlob}`,
`!${rootDir}/{sw-modern,service_worker}.js`,
`${rootDir}/{authorize,onboarding}.html`,
],
{ base: rootDir, allowEmpty: true }
`${modernDir}/**/${filesGlob}`,
compressServiceWorker ? `${rootDir}/sw-modern.js` : undefined,
].filter(Boolean),
{
base: rootDir,
}
)
.pipe(compress === "zopfli" ? zopfli(zopfliOptions) : brotli(brotliOptions))
.pipe(brotli(brotliOptions))
.pipe(gulp.dest(rootDir));
const compressAppModernBrotli = () =>
compressModern(paths.app_output_root, paths.app_output_latest, "brotli");
const compressAppModernZopfli = () =>
compressModern(paths.app_output_root, paths.app_output_latest, "zopfli");
const compressHassioModernBrotli = () =>
compressModern(
const compressAppBrotli = () =>
compressDistBrotli(paths.app_output_root, paths.app_output_latest);
const compressHassioBrotli = () =>
compressDistBrotli(
paths.hassio_output_root,
paths.hassio_output_latest,
"brotli"
);
const compressHassioModernZopfli = () =>
compressModern(
paths.hassio_output_root,
paths.hassio_output_latest,
"zopfli"
false
);
const compressAppOtherBrotli = () =>
compressOther(paths.app_output_root, paths.app_output_latest, "brotli");
const compressAppOtherZopfli = () =>
compressOther(paths.app_output_root, paths.app_output_latest, "zopfli");
const compressHassioOtherBrotli = () =>
compressOther(paths.hassio_output_root, paths.hassio_output_latest, "brotli");
const compressHassioOtherZopfli = () =>
compressOther(paths.hassio_output_root, paths.hassio_output_latest, "zopfli");
gulp.task(
"compress-app",
gulp.parallel(
compressAppModernBrotli,
compressAppOtherBrotli,
compressAppModernZopfli,
compressAppOtherZopfli
)
);
gulp.task(
"compress-hassio",
gulp.parallel(
compressHassioModernBrotli,
compressHassioOtherBrotli,
compressHassioModernZopfli,
compressHassioOtherZopfli
)
);
gulp.task("compress-app", compressAppBrotli);
gulp.task("compress-hassio", compressHassioBrotli);

View File

@@ -67,6 +67,12 @@ function copyPolyfills(staticDir) {
);
}
function copyLoaderJS(staticDir) {
const staticPath = genStaticPath(staticDir);
copyFileDir(npmPath("systemjs/dist/s.min.js"), staticPath("js"));
copyFileDir(npmPath("systemjs/dist/s.min.js.map"), staticPath("js"));
}
function copyFonts(staticDir) {
const staticPath = genStaticPath(staticDir);
// Local fonts
@@ -90,10 +96,6 @@ function copyMapPanel(staticDir) {
npmPath("leaflet/dist/leaflet.css"),
staticPath("images/leaflet/")
);
copyFileDir(
npmPath("leaflet.markercluster/dist/MarkerCluster.css"),
staticPath("images/leaflet/")
);
fs.copySync(
npmPath("leaflet/dist/images"),
staticPath("images/leaflet/images/")
@@ -138,6 +140,8 @@ gulp.task("copy-static-app", async () => {
const staticDir = paths.app_output_static;
// Basic static files
fs.copySync(polyPath("public"), paths.app_output_root);
copyLoaderJS(staticDir);
copyPolyfills(staticDir);
copyFonts(staticDir);
copyTranslations(staticDir);
@@ -160,6 +164,8 @@ gulp.task("copy-static-demo", async () => {
);
// Copy demo static files
fs.copySync(path.resolve(paths.demo_dir, "public"), paths.demo_output_root);
copyLoaderJS(paths.demo_output_static);
copyPolyfills(paths.demo_output_static);
copyMapPanel(paths.demo_output_static);
copyFonts(paths.demo_output_static);
@@ -173,6 +179,8 @@ gulp.task("copy-static-cast", async () => {
fs.copySync(polyPath("public/static"), paths.cast_output_static);
// Copy cast static files
fs.copySync(path.resolve(paths.cast_dir, "public"), paths.cast_output_root);
copyLoaderJS(paths.cast_output_static);
copyPolyfills(paths.cast_output_static);
copyMapPanel(paths.cast_output_static);
copyFonts(paths.cast_output_static);

View File

@@ -1,17 +0,0 @@
import "./app.js";
import "./cast.js";
import "./clean.js";
import "./compress.js";
import "./demo.js";
import "./download-translations.js";
import "./entry-html.js";
import "./fetch-nightly-translations.js";
import "./gallery.js";
import "./gather-static.js";
import "./gen-icons-json.js";
import "./hassio.js";
import "./landing-page.js";
import "./locale-data.js";
import "./rspack.js";
import "./service-worker.js";
import "./translations.js";

View File

@@ -25,7 +25,7 @@ Home Assistant Cast is made up of two separate applications:
### Setting dev variables
Open `src/cast/dev_const.ts` and change `CAST_DEV_APP_ID` to the ID of the app you just created. And set the `CAST_DEV_HASS_URL` to the url of your development machine.
Open `src/cast/dev_const.ts` and change `CAST_DEV_APP_ID` to the ID of the app you just created. And set the `CAST_DEV_HASS_URL` to the url of you development machine.
### Changing configuration

View File

@@ -7,6 +7,7 @@
<%= renderTemplate("../../../src/html/_style_base.html.template") %>
<style>
body {
background-color: white;
font-size: initial;
}
</style>

View File

@@ -3,7 +3,7 @@ import "@material/mwc-list/mwc-list";
import type { ActionDetail } from "@material/mwc-list/mwc-list";
import { mdiCast, mdiCastConnected, mdiViewDashboard } from "@mdi/js";
import type { Auth, Connection } from "home-assistant-js-websocket";
import type { TemplateResult } from "lit";
import type { CSSResultGroup, TemplateResult } from "lit";
import { LitElement, css, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import type { CastManager } from "../../../../src/cast/cast_manager";
@@ -203,72 +203,74 @@ class HcCast extends LitElement {
}
this.connection.close();
location.reload();
} catch (_err: any) {
} catch (err: any) {
alert("Unable to log out!");
}
}
static styles = css`
.center-item {
display: flex;
justify-content: space-around;
}
static get styles(): CSSResultGroup {
return css`
.center-item {
display: flex;
justify-content: space-around;
}
.action-item {
display: flex;
align-items: center;
justify-content: space-between;
}
.action-item {
display: flex;
align-items: center;
justify-content: space-between;
}
.question {
position: relative;
padding: 8px 16px;
}
.question {
position: relative;
padding: 8px 16px;
}
.question:before {
border-radius: 4px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
pointer-events: none;
content: "";
background-color: var(--primary-color);
opacity: 0.12;
will-change: opacity;
}
.question:before {
border-radius: 4px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
pointer-events: none;
content: "";
background-color: var(--primary-color);
opacity: 0.12;
will-change: opacity;
}
.connection,
.connection a {
color: var(--secondary-text-color);
}
.connection,
.connection a {
color: var(--secondary-text-color);
}
mwc-button ha-svg-icon {
margin-right: 8px;
margin-inline-end: 8px;
margin-inline-start: initial;
height: 18px;
}
mwc-button ha-svg-icon {
margin-right: 8px;
margin-inline-end: 8px;
margin-inline-start: initial;
height: 18px;
}
ha-list-item ha-icon,
ha-list-item ha-svg-icon {
padding: 12px;
color: var(--secondary-text-color);
}
ha-list-item ha-icon,
ha-list-item ha-svg-icon {
padding: 12px;
color: var(--secondary-text-color);
}
:host([hide-icons]) ha-icon {
display: none;
}
:host([hide-icons]) ha-icon {
display: none;
}
.spacer {
flex: 1;
}
.spacer {
flex: 1;
}
.card-content a {
color: var(--primary-color);
}
`;
.card-content a {
color: var(--primary-color);
}
`;
}
}
declare global {

View File

@@ -13,7 +13,7 @@ import {
ERR_INVALID_HTTPS_TO_HTTP,
getAuth,
} from "home-assistant-js-websocket";
import type { TemplateResult } from "lit";
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
import type { CastManager } from "../../../../src/cast/cast_manager";
@@ -215,7 +215,7 @@ export class HcConnect extends LitElement {
let url: URL;
try {
url = new URL(value);
} catch (_err: any) {
} catch (err: any) {
this.error = "Invalid URL";
return;
}
@@ -252,7 +252,7 @@ export class HcConnect extends LitElement {
this.loading = false;
return;
} finally {
// Clear url if we have an auth callback in url.
// Clear url if we have a auth callback in url.
if (location.search.includes("auth_callback=1")) {
history.replaceState(null, "", location.pathname);
}
@@ -288,39 +288,41 @@ export class HcConnect extends LitElement {
try {
saveTokens(null);
location.reload();
} catch (_err: any) {
} catch (err: any) {
alert("Unable to log out!");
}
}
static styles = css`
.card-content a {
color: var(--primary-color);
}
.card-actions a {
text-decoration: none;
}
.error {
color: red;
font-weight: bold;
}
static get styles(): CSSResultGroup {
return css`
.card-content a {
color: var(--primary-color);
}
.card-actions a {
text-decoration: none;
}
.error {
color: red;
font-weight: bold;
}
.error a {
color: darkred;
}
.error a {
color: darkred;
}
mwc-button ha-svg-icon {
margin-left: 8px;
}
mwc-button ha-svg-icon {
margin-left: 8px;
}
.spacer {
flex: 1;
}
.spacer {
flex: 1;
}
ha-textfield {
width: 100%;
}
`;
ha-textfield {
width: 100%;
}
`;
}
}
declare global {

View File

@@ -1,6 +1,6 @@
import type { Auth, Connection, HassUser } from "home-assistant-js-websocket";
import { getUser } from "home-assistant-js-websocket";
import type { TemplateResult } from "lit";
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../src/components/ha-card";
@@ -63,94 +63,96 @@ class HcLayout extends LitElement {
}
}
static styles = css`
:host {
display: flex;
min-height: 100%;
align-items: center;
justify-content: center;
flex-direction: column;
}
ha-card {
display: flex;
width: 100%;
max-width: 500px;
}
.layout {
display: flex;
flex-direction: column;
}
.card-header {
color: var(--ha-card-header-color, var(--primary-text-color));
font-family: var(--ha-card-header-font-family, inherit);
font-size: var(--ha-card-header-font-size, 24px);
letter-spacing: -0.012em;
line-height: 32px;
padding: 24px 16px 16px;
display: block;
margin: 0;
}
.hero {
border-radius: 4px 4px 0 0;
}
.subtitle {
font-size: 14px;
color: var(--secondary-text-color);
line-height: initial;
}
.subtitle a {
color: var(--secondary-text-color);
}
:host ::slotted(.card-content:not(:first-child)),
slot:not(:first-child)::slotted(.card-content) {
padding-top: 0px;
margin-top: -8px;
}
:host ::slotted(.section-header) {
font-weight: 500;
padding: 4px 16px;
text-transform: uppercase;
}
:host ::slotted(.card-content) {
padding: 16px;
flex: 1;
}
:host ::slotted(.card-actions) {
border-top: 1px solid #e8e8e8;
padding: 5px 16px;
display: flex;
}
img {
width: 100%;
}
.footer {
text-align: center;
font-size: 12px;
padding: 8px 0 24px;
color: var(--secondary-text-color);
}
.footer a {
color: var(--secondary-text-color);
}
@media all and (max-width: 500px) {
static get styles(): CSSResultGroup {
return css`
:host {
justify-content: flex-start;
min-height: 90%;
margin-bottom: 30px;
display: flex;
min-height: 100%;
align-items: center;
justify-content: center;
flex-direction: column;
}
}
`;
ha-card {
display: flex;
width: 100%;
max-width: 500px;
}
.layout {
display: flex;
flex-direction: column;
}
.card-header {
color: var(--ha-card-header-color, var(--primary-text-color));
font-family: var(--ha-card-header-font-family, inherit);
font-size: var(--ha-card-header-font-size, 24px);
letter-spacing: -0.012em;
line-height: 32px;
padding: 24px 16px 16px;
display: block;
margin: 0;
}
.hero {
border-radius: 4px 4px 0 0;
}
.subtitle {
font-size: 14px;
color: var(--secondary-text-color);
line-height: initial;
}
.subtitle a {
color: var(--secondary-text-color);
}
:host ::slotted(.card-content:not(:first-child)),
slot:not(:first-child)::slotted(.card-content) {
padding-top: 0px;
margin-top: -8px;
}
:host ::slotted(.section-header) {
font-weight: 500;
padding: 4px 16px;
text-transform: uppercase;
}
:host ::slotted(.card-content) {
padding: 16px;
flex: 1;
}
:host ::slotted(.card-actions) {
border-top: 1px solid #e8e8e8;
padding: 5px 16px;
display: flex;
}
img {
width: 100%;
}
.footer {
text-align: center;
font-size: 12px;
padding: 8px 0 24px;
color: var(--secondary-text-color);
}
.footer a {
color: var(--secondary-text-color);
}
@media all and (max-width: 500px) {
:host {
justify-content: flex-start;
min-height: 90%;
margin-bottom: 30px;
}
}
`;
}
}
declare global {

View File

@@ -1,4 +1,4 @@
import type { TemplateResult } from "lit";
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import type { HomeAssistant } from "../../../../src/types";
@@ -24,29 +24,31 @@ class HcLaunchScreen extends LitElement {
`;
}
static styles = css`
:host {
display: block;
height: 100vh;
background-color: #f2f4f9;
font-size: 24px;
}
.container {
display: flex;
flex-direction: column;
text-align: center;
align-items: center;
height: 100%;
justify-content: space-evenly;
}
img {
max-width: 80%;
object-fit: cover;
}
.status {
color: #1d2126;
}
`;
static get styles(): CSSResultGroup {
return css`
:host {
display: block;
height: 100vh;
background-color: #f2f4f9;
font-size: 24px;
}
.container {
display: flex;
flex-direction: column;
text-align: center;
align-items: center;
height: 100%;
justify-content: space-evenly;
}
img {
max-width: 80%;
object-fit: cover;
}
.status {
color: #1d2126;
}
`;
}
}
declare global {

View File

@@ -1,4 +1,10 @@
import { css, html, LitElement, type TemplateResult } from "lit";
import {
css,
type CSSResultGroup,
html,
LitElement,
type TemplateResult,
} from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import type { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
@@ -8,7 +14,6 @@ import "../../../../src/panels/lovelace/views/hui-view";
import "../../../../src/panels/lovelace/views/hui-view-container";
import type { HomeAssistant } from "../../../../src/types";
import "./hc-launch-screen";
import "../../../../src/panels/lovelace/views/hui-view-background";
(window as any).loadCardHelpers = () =>
import("../../../../src/panels/lovelace/custom-card-helpers");
@@ -52,9 +57,11 @@ class HcLovelace extends LitElement {
const background = viewConfig.background || this.lovelaceConfig.background;
return html`
<hui-view-container .hass=${this.hass} .theme=${viewConfig.theme}>
<hui-view-background .hass=${this.hass} .background=${background}>
</hui-view-background>
<hui-view-container
.hass=${this.hass}
.background=${background}
.theme=${viewConfig.theme}
>
<hui-view
.hass=${this.hass}
.lovelace=${lovelace}
@@ -111,18 +118,20 @@ class HcLovelace extends LitElement {
return undefined;
}
static styles = css`
hui-view-container {
display: flex;
position: relative;
min-height: 100vh;
box-sizing: border-box;
}
hui-view-container > * {
flex: 1 1 100%;
max-width: 100%;
}
`;
static get styles(): CSSResultGroup {
return css`
hui-view-container {
display: flex;
position: relative;
min-height: 100vh;
box-sizing: border-box;
}
hui-view {
flex: 1 1 100%;
max-width: 100%;
}
`;
}
}
export interface CastViewChanged {

View File

@@ -3,7 +3,7 @@ import type { Lovelace } from "../../../src/panels/lovelace/types";
import { energyEntities } from "../stubs/entities";
import type { DemoConfig } from "./types";
export const demoConfigs: (() => Promise<DemoConfig>)[] = [
export const demoConfigs: Array<() => Promise<DemoConfig>> = [
() => import("./sections").then((mod) => mod.demoSections),
() => import("./arsaboo").then((mod) => mod.demoArsaboo),
() => import("./teachingbirds").then((mod) => mod.demoTeachingbirds),

View File

@@ -1,4 +1,5 @@
import { mdiTelevision } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, state } from "lit/decorators";
import type { CastManager } from "../../../src/cast/cast_manager";
@@ -66,35 +67,37 @@ class CastDemoRow extends LitElement implements LovelaceRow {
this.style.display = this._castManager ? "" : "none";
}
static styles = css`
:host {
display: flex;
align-items: center;
}
ha-svg-icon {
padding: 8px;
color: var(--paper-item-icon-color);
}
.flex {
flex: 1;
overflow: hidden;
margin-left: 16px;
display: flex;
justify-content: space-between;
align-items: center;
}
.name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
google-cast-launcher {
cursor: pointer;
display: inline-block;
height: 24px;
width: 24px;
}
`;
static get styles(): CSSResultGroup {
return css`
:host {
display: flex;
align-items: center;
}
ha-svg-icon {
padding: 8px;
color: var(--paper-item-icon-color);
}
.flex {
flex: 1;
overflow: hidden;
margin-left: 16px;
display: flex;
justify-content: space-between;
align-items: center;
}
.name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
google-cast-launcher {
cursor: pointer;
display: inline-block;
height: 24px;
width: 24px;
}
`;
}
}
declare global {

View File

@@ -32,7 +32,6 @@ export class HADemoCard extends LitElement implements LovelaceCard {
return this._hidden ? 0 : 2;
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
public setConfig(_config: LovelaceCardConfig) {}
protected render() {

View File

@@ -65,7 +65,6 @@ export class HaDemo extends HomeAssistantAppEl {
mockEntityRegistry(hass, [
{
config_entry_id: "co2signal",
config_subentry_id: null,
device_id: "co2signal",
area_id: null,
disabled_by: null,
@@ -86,7 +85,6 @@ export class HaDemo extends HomeAssistantAppEl {
},
{
config_entry_id: "co2signal",
config_subentry_id: null,
device_id: "co2signal",
area_id: null,
disabled_by: null,

View File

@@ -11,7 +11,6 @@ export const mockConfigEntries = (hass: MockHomeAssistant) => {
supports_remove_device: false,
supports_unload: true,
supports_reconfigure: true,
supported_subentry_types: {},
pref_disable_new_entities: false,
pref_disable_polling: false,
disabled_by: null,

View File

@@ -131,7 +131,6 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
});
}, 1);
// eslint-disable-next-line @typescript-eslint/no-empty-function
return () => {};
}
);

View File

@@ -43,7 +43,7 @@ customElements.whenDefined("hui-root").then(() => {
const index = (ev as CustomEvent).detail.index;
try {
await setDemoConfig(this.hass, this.lovelace!, index);
} catch (_err: any) {
} catch (err: any) {
setDemoConfig(this.hass, this.lovelace!, selectedDemoConfigIndex);
alert("Failed to switch config :-(");
}

View File

@@ -15,7 +15,6 @@ export const mockPersistentNotification = (hass: MockHomeAssistant) => {
},
},
} as PersistentNotificationMessage);
// eslint-disable-next-line @typescript-eslint/no-empty-function
return () => {};
});
};

View File

@@ -15,7 +15,7 @@ import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
const generateMeanStatistics = (
start: Date,
end: Date,
// eslint-disable-next-line default-param-last
// eslint-disable-next-line @typescript-eslint/default-param-last
period: "5minute" | "hour" | "day" | "month" = "hour",
initValue: number,
maxDiff: number
@@ -52,7 +52,7 @@ const generateMeanStatistics = (
const generateSumStatistics = (
start: Date,
end: Date,
// eslint-disable-next-line default-param-last
// eslint-disable-next-line @typescript-eslint/default-param-last
period: "5minute" | "hour" | "day" | "month" = "hour",
initValue: number,
maxDiff: number
@@ -89,7 +89,7 @@ const generateSumStatistics = (
const generateCurvedStatistics = (
start: Date,
end: Date,
// eslint-disable-next-line default-param-last
// eslint-disable-next-line @typescript-eslint/default-param-last
_period: "5minute" | "hour" | "day" | "month" = "hour",
initValue: number,
maxDiff: number,

View File

@@ -11,7 +11,6 @@ export const mockTemplate = (hass: MockHomeAssistant) => {
result: msg.template,
listeners: { all: false, domains: [], entities: [], time: false },
});
// eslint-disable-next-line @typescript-eslint/no-empty-function
return () => {};
});
};

View File

@@ -22,6 +22,5 @@ export const mockTodo = (hass: MockHomeAssistant) => {
},
] as TodoItem[],
}));
// eslint-disable-next-line @typescript-eslint/no-empty-function
hass.mockWS("todo/item/subscribe", (_msg, _hass) => () => {});
};

View File

@@ -1,16 +1,11 @@
// @ts-check
/* eslint-disable import/no-extraneous-dependencies */
import unusedImports from "eslint-plugin-unused-imports";
import globals from "globals";
import tsParser from "@typescript-eslint/parser";
import path from "node:path";
import { fileURLToPath } from "node:url";
import js from "@eslint/js";
import { FlatCompat } from "@eslint/eslintrc";
import tseslint from "typescript-eslint";
import eslintConfigPrettier from "eslint-config-prettier";
import { configs as litConfigs } from "eslint-plugin-lit";
import { configs as wcConfigs } from "eslint-plugin-wc";
const _filename = fileURLToPath(import.meta.url);
const _dirname = path.dirname(_filename);
@@ -20,14 +15,16 @@ const compat = new FlatCompat({
allConfig: js.configs.all,
});
export default tseslint.config(
...compat.extends("airbnb-base", "plugin:lit-a11y/recommended"),
eslintConfigPrettier,
litConfigs["flat/all"],
tseslint.configs.recommended,
tseslint.configs.strict,
tseslint.configs.stylistic,
wcConfigs["flat/recommended"],
export default [
...compat.extends(
"airbnb-base",
"airbnb-typescript/base",
"plugin:@typescript-eslint/recommended",
"plugin:wc/recommended",
"plugin:lit/all",
"plugin:lit-a11y/recommended",
"prettier"
),
{
plugins: {
"unused-imports": unusedImports,
@@ -45,7 +42,7 @@ export default tseslint.config(
Polymer: true,
},
parser: tseslint.parser,
parser: tsParser,
ecmaVersion: 2020,
sourceType: "module",
@@ -53,6 +50,8 @@ export default tseslint.config(
ecmaFeatures: {
modules: true,
},
project: "./tsconfig.json",
},
},
@@ -149,15 +148,15 @@ export default tseslint.config(
},
],
"@typescript-eslint/no-unused-vars": [
"@typescript-eslint/no-unused-vars": "off",
"unused-imports/no-unused-vars": [
"error",
{
args: "all",
argsIgnorePattern: "^_",
caughtErrors: "all",
caughtErrorsIgnorePattern: "^_",
destructuredArrayIgnorePattern: "^_",
vars: "all",
varsIgnorePattern: "^_",
args: "after-used",
argsIgnorePattern: "^_",
ignoreRestSiblings: true,
},
],
@@ -175,16 +174,6 @@ export default tseslint.config(
"lit-a11y/role-has-required-aria-attrs": "error",
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/no-import-type-side-effects": "error",
camelcase: "off",
"@typescript-eslint/no-dynamic-delete": "off",
"@typescript-eslint/no-empty-object-type": [
"error",
{
allowInterfaces: "always",
allowObjectTypes: "always",
},
],
"no-use-before-define": "off",
},
}
);
},
];

View File

@@ -1,10 +1,10 @@
// @ts-check
import tseslint from "typescript-eslint";
import rootConfig from "../eslint.config.mjs";
export default tseslint.config(...rootConfig, {
rules: {
"no-console": "off",
export default [
...rootConfig,
{
rules: {
"no-console": "off",
},
},
});
];

View File

@@ -1,10 +0,0 @@
<svg width="94" height="64" viewBox="0 0 94 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="94" height="64" rx="8" fill="white"/>
<rect x="0.5" y="0.5" width="93" height="63" rx="7.5" stroke="black" stroke-opacity="0.12"/>
<path d="M8 14C8 10.6863 10.6863 8 14 8H33C36.3137 8 39 10.6863 39 14C39 17.3137 36.3137 20 33 20H14C10.6863 20 8 17.3137 8 14Z" fill="black" fill-opacity="0.32"/>
<path d="M8 27C8 25.3431 9.34315 24 11 24H31C32.6569 24 34 25.3431 34 27V29C34 30.6569 32.6569 32 31 32H11C9.34315 32 8 30.6569 8 29V27Z" fill="black" fill-opacity="0.12"/>
<path d="M38 27C38 25.3431 39.3431 24 41 24H83C84.6569 24 86 25.3431 86 27V29C86 30.6569 84.6569 32 83 32H41C39.3431 32 38 30.6569 38 29V27Z" fill="black" fill-opacity="0.12"/>
<path d="M8 39C8 37.3431 9.34315 36 11 36H53C54.6569 36 56 37.3431 56 39V41C56 42.6569 54.6569 44 53 44H11C9.34315 44 8 42.6569 8 41V39Z" fill="black" fill-opacity="0.12"/>
<path d="M60 39C60 37.3431 61.3431 36 63 36H83C84.6569 36 86 37.3431 86 39V41C86 42.6569 84.6569 44 83 44H63C61.3431 44 60 42.6569 60 41V39Z" fill="black" fill-opacity="0.12"/>
<path d="M8 51C8 49.3431 9.34315 48 11 48H31C32.6569 48 34 49.3431 34 51V53C34 54.6569 32.6569 56 31 56H11C9.34315 56 8 54.6569 8 53V51Z" fill="black" fill-opacity="0.12"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,7 +0,0 @@
<svg width="94" height="48" viewBox="0 0 94 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 11C0 9.34315 1.34315 8 3 8H23C24.6569 8 26 9.34315 26 11V13C26 14.6569 24.6569 16 23 16H3C1.34315 16 0 14.6569 0 13V11Z" fill="black" fill-opacity="0.12"/>
<path d="M30 11C30 9.34315 31.3431 8 33 8H91C92.6569 8 94 9.34315 94 11V13C94 14.6569 92.6569 16 91 16H33C31.3431 16 30 14.6569 30 13V11Z" fill="black" fill-opacity="0.12"/>
<path d="M0 23C0 21.3431 1.34315 20 3 20H61C62.6569 20 64 21.3431 64 23V25C64 26.6569 62.6569 28 61 28H3C1.34315 28 0 26.6569 0 25V23Z" fill="black" fill-opacity="0.12"/>
<path d="M68 23C68 21.3431 69.3431 20 71 20H91C92.6569 20 94 21.3431 94 23V25C94 26.6569 92.6569 28 91 28H71C69.3431 28 68 26.6569 68 25V23Z" fill="black" fill-opacity="0.12"/>
<path d="M0 35C0 33.3431 1.34315 32 3 32H23C24.6569 32 26 33.3431 26 35V37C26 38.6569 24.6569 40 23 40H3C1.34315 40 0 38.6569 0 37V35Z" fill="black" fill-opacity="0.12"/>
</svg>

Before

Width:  |  Height:  |  Size: 964 B

View File

@@ -42,7 +42,7 @@ In most cases, Create can be paired with Delete, and Add can be paired with Remo
## Add
An already-existing item.
An already-exisiting item.
For example:

View File

@@ -177,24 +177,26 @@ export class DemoAutomationDescribeAction extends LitElement {
this._action = ev.detail.isValid ? ev.detail.value : undefined;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.action {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
span {
margin-right: 16px;
}
ha-yaml-editor {
width: 50%;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.action {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
span {
margin-right: 16px;
}
ha-yaml-editor {
width: 50%;
}
`;
}
}
declare global {

View File

@@ -98,24 +98,26 @@ export class DemoAutomationDescribeCondition extends LitElement {
this._condition = ev.detail.isValid ? ev.detail.value : undefined;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.condition {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
span {
margin-right: 16px;
}
ha-yaml-editor {
width: 50%;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.condition {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
span {
margin-right: 16px;
}
ha-yaml-editor {
width: 50%;
}
`;
}
}
declare global {

View File

@@ -121,24 +121,26 @@ export class DemoAutomationDescribeTrigger extends LitElement {
this._trigger = ev.detail.isValid ? ev.detail.value : undefined;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.trigger {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
span {
margin-right: 16px;
}
ha-yaml-editor {
width: 50%;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.trigger {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
span {
margin-right: 16px;
}
ha-yaml-editor {
width: 50%;
}
`;
}
}
declare global {

View File

@@ -58,20 +58,22 @@ export class DemoAutomationTraceTimeline extends LitElement {
hass.updateTranslations("config", "en");
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px;
}
.card-content {
display: flex;
}
button {
position: absolute;
top: 0;
right: 0;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px;
}
.card-content {
display: flex;
}
button {
position: absolute;
top: 0;
right: 0;
}
`;
}
}
declare global {

View File

@@ -68,26 +68,28 @@ export class DemoAutomationTrace extends LitElement {
this._selected = { ...this._selected, [sampleIdx]: ev.detail.path };
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px;
}
.card-content {
display: flex;
}
.card-content > * {
margin-right: 16px;
}
.card-content > *:last-child {
margin-right: 0;
}
button {
position: absolute;
top: 0;
right: 0;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px;
}
.card-content {
display: flex;
}
.card-content > * {
margin-right: 16px;
}
.card-content > *:last-child {
margin-right: 0;
}
button {
position: absolute;
top: 0;
right: 0;
}
`;
}
}
declare global {

View File

@@ -19,7 +19,7 @@ The alert offers four severity levels that set a distinctive icon and color.
</ha-alert>
<ha-alert alert-type="warning">
This is a warning alert — check it out!
This is an warning alert — check it out!
</ha-alert>
<ha-alert alert-type="info">
@@ -27,7 +27,7 @@ The alert offers four severity levels that set a distinctive icon and color.
</ha-alert>
<ha-alert alert-type="success">
This is a success alert — check it out!
This is an success alert — check it out!
</ha-alert>
**Note:** This component is by <a href="https://mui.com/components/alert/" rel="noopener noreferrer" target="_blank">MUI</a> and is not documented in the <a href="https://material.io" rel="noopener noreferrer" target="_blank">Material Design guidelines</a>.
@@ -95,7 +95,7 @@ Actions must have a tab index of 0 so that they can be reached by keyboard-only
</ha-alert>
<ha-alert alert-type="warning">
This is a warning alert — check it out!
This is an warning alert — check it out!
</ha-alert>
<ha-alert alert-type="info">
@@ -103,7 +103,7 @@ Actions must have a tab index of 0 so that they can be reached by keyboard-only
</ha-alert>
<ha-alert alert-type="success">
This is a success alert — check it out!
This is an success alert — check it out!
</ha-alert>
```html
@@ -122,37 +122,37 @@ Actions must have a tab index of 0 so that they can be reached by keyboard-only
The `title ` option should not be used without a description.
<ha-alert alert-type="success" title="Success">
This is a success alert — check it out!
This is an success alert — check it out!
</ha-alert>
```html
<ha-alert alert-type="success" title="Success">
This is a success alert — check it out!
This is an success alert — check it out!
</ha-alert>
```
**Dismissable**
<ha-alert alert-type="success" dismissable>
This is a success alert — check it out!
This is an success alert — check it out!
</ha-alert>
```html
<ha-alert alert-type="success" dismissable>
This is a success alert — check it out!
This is an success alert — check it out!
</ha-alert>
```
**Slotted action**
<ha-alert alert-type="success">
This is a success alert — check it out!
This is an success alert — check it out!
<mwc-button slot="action" label="Undo"></mwc-button>
</ha-alert>
```html
<ha-alert alert-type="success">
This is a success alert — check it out!
This is an success alert — check it out!
<mwc-button slot="action" label="Undo"></mwc-button>
</ha-alert>
```

View File

@@ -177,44 +177,46 @@ export class DemoHaAlert extends LitElement {
);
}
static styles = css`
:host {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.dark,
.light {
display: block;
background-color: var(--primary-background-color);
padding: 0 50px;
}
ha-card {
margin: 24px auto;
}
ha-alert {
display: block;
margin: 24px 0;
}
.condition {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
.image {
display: inline-flex;
height: 100%;
align-items: center;
}
img {
max-height: 24px;
width: 24px;
}
mwc-button {
--mdc-theme-primary: var(--primary-text-color);
}
`;
static get styles() {
return css`
:host {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.dark,
.light {
display: block;
background-color: var(--primary-background-color);
padding: 0 50px;
}
ha-card {
margin: 24px auto;
}
ha-alert {
display: block;
margin: 24px 0;
}
.condition {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
.image {
display: inline-flex;
height: 100%;
align-items: center;
}
img {
max-height: 24px;
width: 24px;
}
mwc-button {
--mdc-theme-primary: var(--primary-text-color);
}
`;
}
}
declare global {

View File

@@ -63,18 +63,20 @@ export class DemoHaBar extends LitElement {
`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.warning {
--ha-bar-primary-color: var(--warning-color);
}
.error {
--ha-bar-primary-color: var(--error-color);
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.warning {
--ha-bar-primary-color: var(--warning-color);
}
.error {
--ha-bar-primary-color: var(--error-color);
}
`;
}
}
declare global {

View File

@@ -103,17 +103,19 @@ export class DemoHaChips extends LitElement {
`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.card-content {
display: flex;
flex-direction: column;
align-items: flex-start;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.card-content {
display: flex;
flex-direction: column;
align-items: flex-start;
}
`;
}
}
declare global {

View File

@@ -48,12 +48,14 @@ export class DemoHaCircularProgress extends LitElement {
></ha-card>`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
`;
}
}
declare global {

View File

@@ -14,12 +14,12 @@ import "../../../../src/components/ha-card";
import "../../../../src/components/ha-svg-icon";
import "../../../../src/components/ha-control-button-group";
interface Button {
type Button = {
label: string;
icon?: string;
class?: string;
disabled?: boolean;
}
};
const buttons: Button[] = [
{
@@ -35,10 +35,10 @@ const buttons: Button[] = [
},
];
interface ButtonGroup {
type ButtonGroup = {
vertical?: boolean;
class?: string;
}
};
const buttonGroups: ButtonGroup[] = [
{},
@@ -137,51 +137,53 @@ export class DemoHaBarButton extends LitElement {
`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
label {
font-weight: 600;
}
.custom {
--control-button-icon-color: var(--primary-color);
--control-button-background-color: var(--primary-color);
--control-button-background-opacity: 0.2;
--control-button-border-radius: 18px;
height: 100px;
width: 100px;
}
.custom-group {
--control-button-group-thickness: 100px;
--control-button-group-border-radius: 36px;
--control-button-group-spacing: 20px;
}
.custom-group ha-control-button {
--control-button-border-radius: 18px;
--mdc-icon-size: 32px;
}
.vertical-buttons {
height: 300px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
p.title {
margin-bottom: 12px;
}
.vertical-switches > *:not(:last-child) {
margin-right: 4px;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
label {
font-weight: 600;
}
.custom {
--control-button-icon-color: var(--primary-color);
--control-button-background-color: var(--primary-color);
--control-button-background-opacity: 0.2;
--control-button-border-radius: 18px;
height: 100px;
width: 100px;
}
.custom-group {
--control-button-group-thickness: 100px;
--control-button-group-border-radius: 36px;
--control-button-group-spacing: 20px;
}
.custom-group ha-control-button {
--control-button-border-radius: 18px;
--mdc-icon-size: 32px;
}
.vertical-buttons {
height: 300px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
p.title {
margin-bottom: 12px;
}
.vertical-switches > *:not(:last-child) {
margin-right: 4px;
}
`;
}
}
declare global {

View File

@@ -133,37 +133,39 @@ export class DemoHaCircularSlider extends LitElement {
`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
p.title {
margin-bottom: 12px;
}
ha-control-circular-slider {
--control-circular-slider-color: #ff9800;
}
ha-control-circular-slider[inverted] {
--control-circular-slider-color: #2196f3;
}
ha-control-circular-slider[dual] {
--control-circular-slider-high-color: #2196f3;
--control-circular-slider-low-color: #ff9800;
}
.field {
display: flex;
flex-direction: row;
align-items: center;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
p.title {
margin-bottom: 12px;
}
ha-control-circular-slider {
--control-circular-slider-color: #ff9800;
}
ha-control-circular-slider[inverted] {
--control-circular-slider-color: #2196f3;
}
ha-control-circular-slider[dual] {
--control-circular-slider-high-color: #2196f3;
--control-circular-slider-low-color: #ff9800;
}
.field {
display: flex;
flex-direction: row;
align-items: center;
}
`;
}
}
declare global {

View File

@@ -73,30 +73,32 @@ export class DemoHarControlNumberButtons extends LitElement {
`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
label {
font-weight: 600;
}
.custom {
color: #2196f3;
--control-number-buttons-color: #2196f3;
--control-number-buttons-background-color: #2196f3;
--control-number-buttons-background-opacity: 0.1;
--control-number-buttons-thickness: 100px;
--control-number-buttons-border-radius: 36px;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
label {
font-weight: 600;
}
.custom {
color: #2196f3;
--control-number-buttons-color: #2196f3;
--control-number-buttons-background-color: #2196f3;
--control-number-buttons-background-opacity: 0.1;
--control-number-buttons-thickness: 100px;
--control-number-buttons-border-radius: 36px;
}
`;
}
}
declare global {

View File

@@ -8,19 +8,19 @@ import "../../../../src/components/ha-control-select-menu";
import "../../../../src/components/ha-list-item";
import "../../../../src/components/ha-svg-icon";
interface SelectMenuOptions {
type SelectMenuOptions = {
label: string;
value: string;
icon?: string;
}
};
interface SelectMenu {
type SelectMenu = {
label: string;
icon: string;
class?: string;
disabled?: boolean;
options: SelectMenuOptions[];
}
};
const selects: SelectMenu[] = [
{
@@ -112,30 +112,32 @@ export class DemoHaControlSelectMenu extends LitElement {
`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
label {
font-weight: 600;
}
.custom {
--control-button-icon-color: var(--primary-color);
--control-button-background-color: var(--primary-color);
--control-button-background-opacity: 0.2;
--control-button-border-radius: 18px;
height: 100px;
width: 100px;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
label {
font-weight: 600;
}
.custom {
--control-button-icon-color: var(--primary-color);
--control-button-background-color: var(--primary-color);
--control-button-background-opacity: 0.2;
--control-button-border-radius: 18px;
height: 100px;
width: 100px;
}
`;
}
}
declare global {

View File

@@ -168,40 +168,42 @@ export class DemoHaControlSelect extends LitElement {
`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
label {
font-weight: 600;
}
.custom {
--mdc-icon-size: 24px;
--control-select-color: var(--state-fan-active-color);
--control-select-thickness: 130px;
--control-select-border-radius: 36px;
}
.vertical-selects {
height: 300px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
p.title {
margin-bottom: 12px;
}
.vertical-selects > *:not(:last-child) {
margin-right: 4px;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
label {
font-weight: 600;
}
.custom {
--mdc-icon-size: 24px;
--control-select-color: var(--state-fan-active-color);
--control-select-thickness: 130px;
--control-select-border-radius: 36px;
}
.vertical-selects {
height: 300px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
p.title {
margin-bottom: 12px;
}
.vertical-selects > *:not(:last-child) {
margin-right: 4px;
}
`;
}
}
declare global {

View File

@@ -131,41 +131,43 @@ export class DemoHaBarSlider extends LitElement {
`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
label {
font-weight: 600;
}
.custom {
--control-slider-color: #ffcf4c;
--control-slider-background: #ffcf4c;
--control-slider-background-opacity: 0.2;
--control-slider-thickness: 130px;
--control-slider-border-radius: 36px;
}
.vertical-sliders {
height: 300px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
p.title {
margin-bottom: 12px;
}
.vertical-sliders > *:not(:last-child) {
margin-right: 4px;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
label {
font-weight: 600;
}
.custom {
--control-slider-color: #ffcf4c;
--control-slider-background: #ffcf4c;
--control-slider-background-opacity: 0.2;
--control-slider-thickness: 130px;
--control-slider-border-radius: 36px;
}
.vertical-sliders {
height: 300px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
p.title {
margin-bottom: 12px;
}
.vertical-sliders > *:not(:last-child) {
margin-right: 4px;
}
`;
}
}
declare global {

View File

@@ -99,42 +99,44 @@ export class DemoHaControlSwitch extends LitElement {
`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
label {
font-weight: 600;
}
.custom {
--control-switch-on-color: var(--green-color);
--control-switch-off-color: var(--red-color);
--control-switch-thickness: 130px;
--control-switch-border-radius: 36px;
--control-switch-padding: 6px;
--mdc-icon-size: 24px;
}
.vertical-switches {
height: 300px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
p.title {
margin-bottom: 12px;
}
.vertical-switches > *:not(:last-child) {
margin-right: 4px;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
label {
font-weight: 600;
}
.custom {
--control-switch-on-color: var(--green-color);
--control-switch-off-color: var(--red-color);
--control-switch-thickness: 130px;
--control-switch-border-radius: 36px;
--control-switch-padding: 6px;
--mdc-icon-size: 24px;
}
.vertical-switches {
height: 300px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
p.title {
margin-bottom: 12px;
}
.vertical-switches > *:not(:last-child) {
margin-right: 4px;
}
`;
}
}
declare global {

View File

@@ -143,11 +143,13 @@ export class DemoHaExpansionPanel extends LitElement {
`;
}
static styles = css`
ha-expansion-panel {
margin: -16px;
}
`;
static get styles() {
return css`
ha-expansion-panel {
margin: -16px;
}
`;
}
}
declare global {

View File

@@ -20,7 +20,7 @@ export class DemoHaFaded extends LitElement {
<ha-faded><span>${LONG_TEXT}</span></ha-faded>
<h3>No text</h3>
<ha-faded><span></span></ha-faded>
<h3>Small text</h3>
<h3>Smal text</h3>
<ha-faded><span>${SMALL_TEXT}</span></ha-faded>
<h3>Long text in markdown</h3>
<ha-faded>
@@ -61,12 +61,14 @@ export class DemoHaFaded extends LitElement {
`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
`;
}
}
declare global {

View File

@@ -48,7 +48,6 @@ const DEVICES: DeviceRegistryEntry[] = [
area_id: "bedroom",
configuration_url: null,
config_entries: ["config_entry_1"],
config_entries_subentries: {},
connections: [],
disabled_by: null,
entry_type: null,
@@ -72,7 +71,6 @@ const DEVICES: DeviceRegistryEntry[] = [
area_id: "backyard",
configuration_url: null,
config_entries: ["config_entry_2"],
config_entries_subentries: {},
connections: [],
disabled_by: null,
entry_type: null,
@@ -96,7 +94,6 @@ const DEVICES: DeviceRegistryEntry[] = [
area_id: null,
configuration_url: null,
config_entries: ["config_entry_3"],
config_entries_subentries: {},
connections: [],
disabled_by: null,
entry_type: null,
@@ -127,8 +124,6 @@ const AREAS: AreaRegistryEntry[] = [
picture: null,
aliases: [],
labels: [],
temperature_entity_id: null,
humidity_entity_id: null,
created_at: 0,
modified_at: 0,
},
@@ -140,8 +135,6 @@ const AREAS: AreaRegistryEntry[] = [
picture: null,
aliases: [],
labels: [],
temperature_entity_id: null,
humidity_entity_id: null,
created_at: 0,
modified_at: 0,
},
@@ -153,8 +146,6 @@ const AREAS: AreaRegistryEntry[] = [
picture: null,
aliases: [],
labels: [],
temperature_entity_id: null,
humidity_entity_id: null,
created_at: 0,
modified_at: 0,
},

View File

@@ -76,7 +76,7 @@ export class DemoHaHsColorPicker extends LitElement {
@change=${this._saturationChanged}
>
</ha-slider>
<p>Color Brightness : ${this.brightness}</p>
<p>Color Brighness : ${this.brightness}</p>
<ha-slider
labeled
step="1"
@@ -91,25 +91,27 @@ export class DemoHaHsColorPicker extends LitElement {
`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.card-content {
display: flex;
align-items: center;
flex-direction: column;
}
ha-hs-color-picker {
width: 400px;
}
.value {
font-size: 22px;
font-weight: bold;
margin: 0 0 12px 0;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.card-content {
display: flex;
align-items: center;
flex-direction: column;
}
ha-hs-color-picker {
width: 400px;
}
.value {
font-size: 22px;
font-weight: bold;
margin: 0 0 12px 0;
}
`;
}
}
declare global {

View File

@@ -95,23 +95,25 @@ export class DemoHaLabelBadge extends LitElement {
`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-left: 16px;
background-color: var(--markdown-code-background-color);
padding: 8px;
}
.badge {
display: flex;
flex-direction: row;
margin-bottom: 16px;
align-items: center;
}
`;
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-left: 16px;
background-color: var(--markdown-code-background-color);
padding: 8px;
}
.badge {
display: flex;
flex-direction: row;
margin-bottom: 16px;
align-items: center;
}
`;
}
}
declare global {

View File

@@ -1,3 +0,0 @@
---
title: Select box
---

View File

@@ -1,152 +0,0 @@
import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-select-box";
import type { SelectBoxOption } from "../../../../src/components/ha-select-box";
const basicOptions: SelectBoxOption[] = [
{
value: "text-only",
label: "Text only",
},
{
value: "card",
label: "Card",
},
{
value: "disabled",
label: "Disabled option",
disabled: true,
},
];
const fullOptions: SelectBoxOption[] = [
{
value: "text-only",
label: "Text only",
description: "Only text, no border and background",
image: "/images/select_box/text_only.svg",
},
{
value: "card",
label: "Card",
description: "With border and background",
image: "/images/select_box/card.svg",
},
{
value: "disabled",
label: "Disabled",
description: "Option that can not be selected",
disabled: true,
},
];
const selects: {
id: string;
label: string;
class?: string;
options: SelectBoxOption[];
disabled?: boolean;
}[] = [
{
id: "basic",
label: "Basic",
options: basicOptions,
},
{
id: "full",
label: "With description and image",
options: fullOptions,
},
];
@customElement("demo-components-ha-select-box")
export class DemoHaSelectBox extends LitElement {
@state() private value?: string = "off";
handleValueChanged(e: CustomEvent) {
this.value = e.detail.value as string;
}
protected render(): TemplateResult {
return html`
${repeat(selects, (select) => {
const { id, label, options } = select;
return html`
<ha-card>
<div class="card-content">
<label id=${id}>${label}</label>
<ha-select-box
.value=${this.value}
.options=${options}
@value-changed=${this.handleValueChanged}
>
</ha-select-box>
</div>
</ha-card>
`;
})}
<ha-card>
<div class="card-content">
<p class="title"><b>Column layout</b></p>
<div class="vertical-selects">
${repeat(selects, (select) => {
const { options } = select;
return html`
<ha-select-box
.value=${this.value}
.options=${options}
.maxColumns=${1}
@value-changed=${this.handleValueChanged}
>
</ha-select-box>
`;
})}
</div>
</div>
</ha-card>
`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
pre {
margin-top: 0;
margin-bottom: 8px;
}
p {
margin: 0;
}
label {
font-weight: 600;
margin-bottom: 8px;
display: block;
}
.custom {
--mdc-icon-size: 24px;
--control-select-color: var(--state-fan-active-color);
--control-select-thickness: 130px;
--control-select-border-radius: 36px;
}
p.title {
margin-bottom: 12px;
}
.vertical-selects ha-select-box {
display: block;
margin-bottom: 24px;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"demo-components-ha-select-box": DemoHaSelectBox;
}
}

View File

@@ -47,7 +47,6 @@ const DEVICES: DeviceRegistryEntry[] = [
area_id: "bedroom",
configuration_url: null,
config_entries: ["config_entry_1"],
config_entries_subentries: {},
connections: [],
disabled_by: null,
entry_type: null,
@@ -71,7 +70,6 @@ const DEVICES: DeviceRegistryEntry[] = [
area_id: "backyard",
configuration_url: null,
config_entries: ["config_entry_2"],
config_entries_subentries: {},
connections: [],
disabled_by: null,
entry_type: null,
@@ -95,7 +93,6 @@ const DEVICES: DeviceRegistryEntry[] = [
area_id: null,
configuration_url: null,
config_entries: ["config_entry_3"],
config_entries_subentries: {},
connections: [],
disabled_by: null,
entry_type: null,
@@ -126,8 +123,6 @@ const AREAS: AreaRegistryEntry[] = [
picture: null,
aliases: [],
labels: [],
temperature_entity_id: null,
humidity_entity_id: null,
created_at: 0,
modified_at: 0,
},
@@ -139,8 +134,6 @@ const AREAS: AreaRegistryEntry[] = [
picture: null,
aliases: [],
labels: [],
temperature_entity_id: null,
humidity_entity_id: null,
created_at: 0,
modified_at: 0,
},
@@ -152,8 +145,6 @@ const AREAS: AreaRegistryEntry[] = [
picture: null,
aliases: [],
labels: [],
temperature_entity_id: null,
humidity_entity_id: null,
created_at: 0,
modified_at: 0,
},

View File

@@ -48,25 +48,27 @@ export class DemoHaTip extends LitElement {
);
}
static styles = css`
:host {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.dark,
.light {
display: block;
background-color: var(--primary-background-color);
padding: 0 50px;
}
ha-tip {
margin-bottom: 14px;
}
ha-card {
margin: 24px auto;
}
`;
static get styles() {
return css`
:host {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.dark,
.light {
display: block;
background-color: var(--primary-background-color);
padding: 0 50px;
}
ha-tip {
margin-bottom: 14px;
}
ha-card {
margin: 24px auto;
}
`;
}
}
declare global {

View File

@@ -1,30 +0,0 @@
---
title: Tooltip
---
A tooltip's target is its _first child element_, so you should only wrap one element inside of the tooltip. If you need the tooltip to show up for multiple elements, nest them inside a container first.
Tooltips use `display: contents` so they won't interfere with how elements are positioned in a flex or grid layout.
<ha-tooltip content="This is a tooltip">
<ha-button>Hover Me</ha-button>
</ha-tooltip>
```
<ha-tooltip content="This is a tooltip">
<ha-button>Hover Me</ha-button>
</ha-tooltip>
```
## Documentation
This element is based on sholace `sl-tooltip` it only sets some css tokens and has a custom show/hide animation.
<a href="https://shoelace.style/components/tooltip" target="_blank" rel="noopener noreferrer">Shoelace documentation</a>
### HA style tokens
In your theme settings use this without the prefixed `--`.
- `--ha-tooltip-border-radius` (Default: 4px)
- `--ha-tooltip-arrow-size` (Default: 8px)

View File

@@ -1,2 +0,0 @@
import "../../../../src/components/ha-tooltip";
import "../../../../src/components/ha-button";

View File

@@ -100,30 +100,32 @@ export class DemoDateTimeDateTimeNumeric extends LitElement {
`;
}
static styles = css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 900px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
static get styles() {
return css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 900px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
.container > div {
flex-grow: 1;
width: 20%;
}
`;
.container > div {
flex-grow: 1;
width: 20%;
}
`;
}
}
declare global {

View File

@@ -100,30 +100,32 @@ export class DemoDateTimeDateTimeSeconds extends LitElement {
`;
}
static styles = css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 900px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
static get styles() {
return css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 900px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
.container > div {
flex-grow: 1;
width: 20%;
}
`;
.container > div {
flex-grow: 1;
width: 20%;
}
`;
}
}
declare global {

View File

@@ -100,30 +100,32 @@ export class DemoDateTimeDateTimeShortYear extends LitElement {
`;
}
static styles = css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 900px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
static get styles() {
return css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 900px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
.container > div {
flex-grow: 1;
width: 20%;
}
`;
.container > div {
flex-grow: 1;
width: 20%;
}
`;
}
}
declare global {

View File

@@ -100,30 +100,32 @@ export class DemoDateTimeDateTimeShort extends LitElement {
`;
}
static styles = css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 900px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
static get styles() {
return css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 900px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
.container > div {
flex-grow: 1;
width: 20%;
}
`;
.container > div {
flex-grow: 1;
width: 20%;
}
`;
}
}
declare global {

View File

@@ -100,30 +100,32 @@ export class DemoDateTimeDateTime extends LitElement {
`;
}
static styles = css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 900px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
static get styles() {
return css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 900px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
.container > div {
flex-grow: 1;
width: 20%;
}
`;
.container > div {
flex-grow: 1;
width: 20%;
}
`;
}
}
declare global {

View File

@@ -90,26 +90,28 @@ export class DemoDateTimeDate extends LitElement {
`;
}
static styles = css`
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 600px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
static get styles() {
return css`
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 600px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
.container > div {
flex-grow: 1;
width: 20%;
}
`;
.container > div {
flex-grow: 1;
width: 20%;
}
`;
}
}
declare global {

View File

@@ -100,30 +100,32 @@ export class DemoDateTimeTimeSeconds extends LitElement {
`;
}
static styles = css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 600px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
static get styles() {
return css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 600px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
.container > div {
flex-grow: 1;
width: 20%;
}
`;
.container > div {
flex-grow: 1;
width: 20%;
}
`;
}
}
declare global {

View File

@@ -100,30 +100,32 @@ export class DemoDateTimeTimeWeekday extends LitElement {
`;
}
static styles = css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 800px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
static get styles() {
return css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 800px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
.container > div {
flex-grow: 1;
width: 20%;
}
`;
.container > div {
flex-grow: 1;
width: 20%;
}
`;
}
}
declare global {

View File

@@ -100,30 +100,32 @@ export class DemoDateTimeTime extends LitElement {
`;
}
static styles = css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 600px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
static get styles() {
return css`
ha-control-select {
max-width: 800px;
margin: 12px auto;
}
.header {
font-weight: bold;
}
.center {
text-align: center;
}
.container {
max-width: 600px;
margin: 12px auto;
display: flex;
align-items: center;
justify-content: space-evenly;
}
.container > div {
flex-grow: 1;
width: 20%;
}
`;
.container > div {
flex-grow: 1;
width: 20%;
}
`;
}
}
declare global {

View File

@@ -321,13 +321,13 @@ function createEntity(
};
}
interface EntityRowData {
type EntityRowData = {
stateObj: HassEntity;
entity_id: string;
state: string;
device_class?: string;
domain: string;
}
};
function createRowData(stateObj: HassEntity): EntityRowData {
return {
@@ -429,15 +429,17 @@ export class DemoEntityState extends LitElement {
`;
}
static styles = css`
.color {
display: block;
height: 20px;
width: 20px;
border-radius: 10px;
background-color: rgb(--color);
}
`;
static get styles() {
return css`
.color {
display: block;
height: 20px;
width: 20px;
border-radius: 10px;
background-color: rgb(--color);
}
`;
}
}
declare global {

View File

@@ -76,12 +76,14 @@ export class DemoMiscMarkdown extends LitElement {
`;
}
static styles = css`
ha-card {
margin: 12px;
padding: 12px;
}
`;
static get styles() {
return css`
ha-card {
margin: 12px;
padding: 12px;
}
`;
}
}
declare global {

View File

@@ -32,8 +32,6 @@ const createConfigEntry = (
supports_remove_device: false,
supports_unload: true,
supports_reconfigure: true,
supported_subentry_types: {},
num_subentries: 0,
disabled_by: null,
pref_disable_new_entities: false,
pref_disable_polling: false,
@@ -137,12 +135,12 @@ const configFlows: DataEntryFlowProgressExtended[] = [
},
];
const configEntries: {
const configEntries: Array<{
items: ConfigEntryExtended[];
is_custom?: boolean;
disabled?: boolean;
highlight?: string;
}[] = [
}> = [
{ items: [loadedEntry] },
{ items: [configPanelEntry] },
{ items: [optionsFlowEntry] },
@@ -190,7 +188,6 @@ const createEntityRegistryEntries = (
): EntityRegistryEntry[] => [
{
config_entry_id: item.entry_id,
config_subentry_id: null,
device_id: "mock-device-id",
area_id: null,
disabled_by: null,
@@ -217,7 +214,6 @@ const createDeviceRegistryEntries = (
{
entry_type: null,
config_entries: [item.entry_id],
config_entries_subentries: {},
connections: [],
manufacturer: "ESPHome",
model: "Mock Device",
@@ -353,24 +349,26 @@ export class DemoIntegrationCard extends LitElement {
this.isCloud = !this.isCloud;
}
static styles = css`
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
grid-gap: 8px 8px;
padding: 8px 16px 16px;
margin-bottom: 16px;
}
static get styles() {
return css`
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
grid-gap: 8px 8px;
padding: 8px 16px 16px;
margin-bottom: 16px;
}
.container > * {
max-width: 500px;
}
.container > * {
max-width: 500px;
}
ha-formfield {
margin: 8px 0;
display: block;
}
`;
ha-formfield {
margin: 8px 0;
display: block;
}
`;
}
}
declare global {

View File

@@ -1,4 +1,16 @@
import { globIterate } from "glob";
import { availableParallelism } from "node:os";
import "./build-scripts/gulp/index.mjs";
process.env.UV_THREADPOOL_SIZE = availableParallelism();
const gulpImports = [];
for await (const gulpModule of globIterate("build-scripts/gulp/*.?(c|m)js", {
dotRelative: true,
})) {
gulpImports.push(import(gulpModule));
}
// Since all tasks are currently registered with gulp.task(), this is enough
// If any are converted to named exports, need to loop and aggregate exports here
await Promise.all(gulpImports);

View File

@@ -1,7 +1,7 @@
import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js";
import type { PropertyValues, TemplateResult } from "lit";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
@@ -211,33 +211,35 @@ export class HassioAddonStore extends LitElement {
this._filter = e.detail.value;
}
static styles = css`
hassio-addon-repository {
margin-top: 24px;
}
.search {
position: sticky;
top: 0;
z-index: 2;
}
search-input {
display: block;
--mdc-text-field-fill-color: var(--sidebar-background-color);
--mdc-text-field-idle-line-color: var(--divider-color);
}
.advanced {
padding: 12px;
display: flex;
flex-wrap: wrap;
color: var(--primary-text-color);
}
.advanced a {
margin-left: 0.5em;
margin-inline-start: 0.5em;
margin-inline-end: initial;
color: var(--primary-color);
}
`;
static get styles(): CSSResultGroup {
return css`
hassio-addon-repository {
margin-top: 24px;
}
.search {
position: sticky;
top: 0;
z-index: 2;
}
search-input {
display: block;
--mdc-text-field-fill-color: var(--sidebar-background-color);
--mdc-text-field-idle-line-color: var(--divider-color);
}
.advanced {
padding: 12px;
display: flex;
flex-wrap: wrap;
color: var(--primary-text-color);
}
.advanced a {
margin-left: 0.5em;
margin-inline-start: 0.5em;
margin-inline-end: initial;
color: var(--primary-color);
}
`;
}
}
declare global {

View File

@@ -299,7 +299,7 @@ class HassioAddonConfig extends LitElement {
if (this.addon.schema && this._canShowSchema && !this._yamlMode) {
this._valid = true;
this._configHasChanged = true;
this._options = ev.detail.value;
this._options! = ev.detail.value;
} else {
this._configHasChanged = true;
this._valid = ev.detail.isValid;

View File

@@ -151,7 +151,7 @@ class HassioAddonNetwork extends LitElement {
private async _configChanged(ev: CustomEvent): Promise<void> {
this._configHasChanged = true;
this._config = ev.detail.value;
this._config! = ev.detail.value;
}
private async _resetTapped(ev: CustomEvent): Promise<void> {

View File

@@ -1,5 +1,5 @@
import { mdiHelpCircle } from "@mdi/js";
import type { TemplateResult } from "lit";
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../src/components/ha-svg-icon";
@@ -64,84 +64,86 @@ class HassioCardContent extends LitElement {
`;
}
static styles = css`
:host {
direction: ltr;
}
static get styles(): CSSResultGroup {
return css`
:host {
direction: ltr;
}
ha-svg-icon {
margin-right: 24px;
margin-left: 8px;
margin-top: 12px;
float: left;
color: var(--secondary-text-color);
}
ha-svg-icon.update {
color: var(--warning-color);
}
ha-svg-icon.running,
ha-svg-icon.installed {
color: var(--success-color);
}
ha-svg-icon.hassupdate,
ha-svg-icon.backup {
color: var(--paper-item-icon-color);
}
ha-svg-icon.not_available {
color: var(--error-color);
}
.title {
color: var(--primary-text-color);
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.addition {
color: var(--secondary-text-color);
overflow: hidden;
position: relative;
height: 2.4em;
line-height: 1.2em;
}
.icon_image img {
max-height: 40px;
max-width: 40px;
margin-top: 4px;
margin-right: 16px;
float: left;
}
.icon_image.stopped,
.icon_image.not_available {
filter: grayscale(1);
}
.dot {
position: absolute;
background-color: var(--warning-color);
width: 12px;
height: 12px;
top: 8px;
right: 8px;
border-radius: 50%;
}
.topbar {
position: absolute;
width: 100%;
height: 2px;
top: 0;
left: 0;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.topbar.installed {
background-color: var(--primary-color);
}
.topbar.update {
background-color: var(--accent-color);
}
.topbar.unavailable {
background-color: var(--error-color);
}
`;
ha-svg-icon {
margin-right: 24px;
margin-left: 8px;
margin-top: 12px;
float: left;
color: var(--secondary-text-color);
}
ha-svg-icon.update {
color: var(--warning-color);
}
ha-svg-icon.running,
ha-svg-icon.installed {
color: var(--success-color);
}
ha-svg-icon.hassupdate,
ha-svg-icon.backup {
color: var(--paper-item-icon-color);
}
ha-svg-icon.not_available {
color: var(--error-color);
}
.title {
color: var(--primary-text-color);
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.addition {
color: var(--secondary-text-color);
overflow: hidden;
position: relative;
height: 2.4em;
line-height: 1.2em;
}
.icon_image img {
max-height: 40px;
max-width: 40px;
margin-top: 4px;
margin-right: 16px;
float: left;
}
.icon_image.stopped,
.icon_image.not_available {
filter: grayscale(1);
}
.dot {
position: absolute;
background-color: var(--warning-color);
width: 12px;
height: 12px;
top: 8px;
right: 8px;
border-radius: 50%;
}
.topbar {
position: absolute;
width: 100%;
height: 2px;
top: 0;
left: 0;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.topbar.installed {
background-color: var(--primary-color);
}
.topbar.update {
background-color: var(--accent-color);
}
.topbar.unavailable {
background-color: var(--error-color);
}
`;
}
}
declare global {

View File

@@ -1,6 +1,8 @@
import type { IFuseOptions } from "fuse.js";
import Fuse from "fuse.js";
import { stripDiacritics } from "../../../src/common/string/strip-diacritics";
import type { StoreAddon } from "../../../src/data/supervisor/store";
import { getStripDiacriticsFn } from "../../../src/util/fuse";
export function filterAndSort(addons: StoreAddon[], filter: string) {
const options: IFuseOptions<StoreAddon> = {
@@ -8,8 +10,8 @@ export function filterAndSort(addons: StoreAddon[], filter: string) {
isCaseSensitive: false,
minMatchCharLength: Math.min(filter.length, 2),
threshold: 0.2,
ignoreDiacritics: true,
getFn: getStripDiacriticsFn,
};
const fuse = new Fuse(addons, options);
return fuse.search(filter).map((result) => result.item);
return fuse.search(stripDiacritics(filter)).map((result) => result.item);
}

View File

@@ -1,7 +1,7 @@
import { mdiFolderUpload } from "@mdi/js";
import type { TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, state } from "lit/decorators";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/ha-circular-progress";
import "../../../src/components/ha-file-upload";
@@ -10,12 +10,10 @@ import { uploadBackup } from "../../../src/data/hassio/backup";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import type { HomeAssistant } from "../../../src/types";
import type { LocalizeFunc } from "../../../src/common/translations/localize";
declare global {
interface HASSDomEvents {
"hassio-backup-uploaded": { backup: HassioBackup };
"backup-cleared": undefined;
"backup-uploaded": { backup: HassioBackup };
}
}
@@ -23,8 +21,6 @@ declare global {
export class HassioUploadBackup extends LitElement {
public hass?: HomeAssistant;
@property({ attribute: false }) public localize?: LocalizeFunc;
@state() public value: string | null = null;
@state() private _uploading = false;
@@ -36,26 +32,13 @@ export class HassioUploadBackup extends LitElement {
.uploading=${this._uploading}
.icon=${mdiFolderUpload}
accept="application/x-tar"
.label=${this.localize?.(
"ui.panel.page-onboarding.restore.upload_backup"
) || "Upload backup"}
.supports=${this.localize?.(
"ui.panel.page-onboarding.restore.upload_supports"
) || "Supports .TAR files"}
.secondary=${this.localize?.(
"ui.panel.page-onboarding.restore.upload_drop"
) || "Or drop your file here"}
label="Upload backup"
supports="Supports .TAR files"
@file-picked=${this._uploadFile}
@files-cleared=${this._clear}
></ha-file-upload>
`;
}
private _clear() {
this.value = null;
fireEvent(this, "backup-cleared");
}
private async _uploadFile(ev) {
const file = ev.detail.files[0];
@@ -70,7 +53,7 @@ export class HassioUploadBackup extends LitElement {
this._uploading = true;
try {
const backup = await uploadBackup(this.hass, file);
fireEvent(this, "hassio-backup-uploaded", { backup: backup.data });
fireEvent(this, "backup-uploaded", { backup: backup.data });
} catch (err: any) {
showAlertDialog(this, {
title: "Upload failed",

View File

@@ -1,10 +1,11 @@
import { mdiFolder, mdiPuzzle } from "@mdi/js";
import type { TemplateResult } from "lit";
import type { CSSResultGroup, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query } from "lit/decorators";
import { atLeastVersion } from "../../../src/common/config/version";
import { formatDate } from "../../../src/common/datetime/format_date";
import { formatDateTime } from "../../../src/common/datetime/format_date_time";
import type { LocalizeFunc } from "../../../src/common/translations/localize";
import "../../../src/components/ha-checkbox";
import "../../../src/components/ha-formfield";
import "../../../src/components/ha-textfield";
@@ -18,10 +19,13 @@ import type {
} from "../../../src/data/hassio/backup";
import type { Supervisor } from "../../../src/data/supervisor/supervisor";
import { mdiHomeAssistant } from "../../../src/resources/home-assistant-logo-svg";
import type { HomeAssistant } from "../../../src/types";
import type { HomeAssistant, TranslationDict } from "../../../src/types";
import "./supervisor-formfield-label";
import type { HaTextField } from "../../../src/components/ha-textfield";
type BackupOrRestoreKey = keyof TranslationDict["supervisor"]["backup"] &
keyof TranslationDict["ui"]["panel"]["page-onboarding"]["restore"];
interface CheckboxItem {
slug: string;
checked: boolean;
@@ -61,7 +65,9 @@ const _computeAddons = (addons): AddonCheckboxItem[] =>
@customElement("supervisor-backup-content")
export class SupervisorBackupContent extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public localize?: LocalizeFunc;
@property({ attribute: false }) public supervisor?: Supervisor;
@@ -109,6 +115,10 @@ export class SupervisorBackupContent extends LitElement {
this._focusTarget?.focus();
}
private _localize = (key: BackupOrRestoreKey) =>
this.supervisor?.localize(`backup.${key}`) ||
this.localize!(`ui.panel.page-onboarding.restore.${key}`);
protected render() {
if (!this.onboarding && !this.supervisor) {
return nothing;
@@ -122,8 +132,8 @@ export class SupervisorBackupContent extends LitElement {
${this.backup
? html`<div class="details">
${this.backup.type === "full"
? this.supervisor?.localize("backup.full_backup")
: this.supervisor?.localize("backup.partial_backup")}
? this._localize("full_backup")
: this._localize("partial_backup")}
(${Math.ceil(this.backup.size * 10) / 10 + " MB"})<br />
${this.hass
? formatDateTime(
@@ -135,7 +145,7 @@ export class SupervisorBackupContent extends LitElement {
</div>`
: html`<ha-textfield
name="backupName"
.label=${this.supervisor?.localize("backup.name")}
.label=${this._localize("name")}
.value=${this.backupName}
@change=${this._handleTextValueChanged}
>
@@ -143,13 +153,11 @@ export class SupervisorBackupContent extends LitElement {
${!this.backup || this.backup.type === "full"
? html`<div class="sub-header">
${!this.backup
? this.supervisor?.localize("backup.type")
: this.supervisor?.localize("backup.select_type")}
? this._localize("type")
: this._localize("select_type")}
</div>
<div class="backup-types">
<ha-formfield
.label=${this.supervisor?.localize("backup.full_backup")}
>
<ha-formfield .label=${this._localize("full_backup")}>
<ha-radio
@change=${this._handleRadioValueChanged}
value="full"
@@ -158,9 +166,7 @@ export class SupervisorBackupContent extends LitElement {
>
</ha-radio>
</ha-formfield>
<ha-formfield
.label=${this.supervisor?.localize("backup.partial_backup")}
>
<ha-formfield .label=${this._localize("partial_backup")}>
<ha-radio
@change=${this._handleRadioValueChanged}
value="partial"
@@ -180,13 +186,12 @@ export class SupervisorBackupContent extends LitElement {
.iconPath=${mdiHomeAssistant}
.version=${this.backup
? this.backup.homeassistant
: this.hass?.config.version}
: this.hass.config.version}
>
</supervisor-formfield-label>`}
>
<ha-checkbox
.checked=${this.onboarding || this.homeAssistant}
.disabled=${this.onboarding}
.checked=${this.homeAssistant}
@change=${this._toggleHomeAssistant}
>
</ha-checkbox>
@@ -196,7 +201,7 @@ export class SupervisorBackupContent extends LitElement {
? html`
<ha-formfield
.label=${html`<supervisor-formfield-label
.label=${this.supervisor?.localize("backup.folders")}
.label=${this._localize("folders")}
.iconPath=${mdiFolder}
>
</supervisor-formfield-label>`}
@@ -216,7 +221,7 @@ export class SupervisorBackupContent extends LitElement {
? html`
<ha-formfield
.label=${html`<supervisor-formfield-label
.label=${this.supervisor?.localize("backup.addons")}
.label=${this._localize("addons")}
.iconPath=${mdiPuzzle}
>
</supervisor-formfield-label>`}
@@ -241,7 +246,7 @@ export class SupervisorBackupContent extends LitElement {
${!this.backup
? html`<ha-formfield
class="password"
.label=${this.supervisor?.localize("backup.password_protection")}
.label=${this._localize("password_protection")}
>
<ha-checkbox
.checked=${this.backupHasPassword}
@@ -253,7 +258,7 @@ export class SupervisorBackupContent extends LitElement {
${this.backupHasPassword
? html`
<ha-password-field
.label=${this.supervisor?.localize("backup.password")}
.label=${this._localize("password")}
name="backupPassword"
.value=${this.backupPassword}
@change=${this._handleTextValueChanged}
@@ -261,7 +266,7 @@ export class SupervisorBackupContent extends LitElement {
</ha-password-field>
${!this.backup
? html`<ha-password-field
.label=${this.supervisor?.localize("backup.confirm_password")}
.label=${this._localize("confirm_password")}
name="confirmBackupPassword"
.value=${this.confirmBackupPassword}
@change=${this._handleTextValueChanged}
@@ -277,57 +282,59 @@ export class SupervisorBackupContent extends LitElement {
this.homeAssistant = !this.homeAssistant;
}
static styles = css`
.partial-picker ha-formfield {
display: block;
}
.partial-picker ha-checkbox {
--mdc-checkbox-touch-target-size: 32px;
}
.partial-picker {
display: block;
margin: 0px -6px;
}
supervisor-formfield-label {
display: inline-flex;
align-items: center;
}
hr {
border-color: var(--divider-color);
border-bottom: none;
margin: 16px 0;
}
.details {
color: var(--secondary-text-color);
}
.section-content {
display: flex;
flex-direction: column;
margin-left: 30px;
margin-inline-start: 30px;
margin-inline-end: initial;
}
ha-formfield.password {
display: block;
margin: 0 -14px -16px;
}
.backup-types {
display: flex;
margin-left: -13px;
margin-inline-start: -13px;
margin-inline-end: initial;
}
.sub-header {
margin-top: 8px;
}
`;
static get styles(): CSSResultGroup {
return css`
.partial-picker ha-formfield {
display: block;
}
.partial-picker ha-checkbox {
--mdc-checkbox-touch-target-size: 32px;
}
.partial-picker {
display: block;
margin: 0px -6px;
}
supervisor-formfield-label {
display: inline-flex;
align-items: center;
}
hr {
border-color: var(--divider-color);
border-bottom: none;
margin: 16px 0;
}
.details {
color: var(--secondary-text-color);
}
.section-content {
display: flex;
flex-direction: column;
margin-left: 30px;
margin-inline-start: 30px;
margin-inline-end: initial;
}
ha-formfield.password {
display: block;
margin: 0 -14px -16px;
}
.backup-types {
display: flex;
margin-left: -13px;
margin-inline-start: -13px;
margin-inline-end: initial;
}
.sub-header {
margin-top: 8px;
}
`;
}
public backupDetails():
| HassioPartialBackupCreateParams
| HassioFullBackupCreateParams {
const data: any = {};
if (!this.backup && this.hass) {
if (!this.backup) {
data.name =
this.backupName ||
formatDate(new Date(), this.hass.locale, this.hass.config);
@@ -357,9 +364,7 @@ export class SupervisorBackupContent extends LitElement {
if (folders?.length) {
data.folders = folders;
}
// onboarding needs at least homeassistant to restore
data.homeassistant = this.onboarding || this.homeAssistant;
data.homeassistant = this.homeAssistant;
return data;
}
@@ -381,7 +386,6 @@ export class SupervisorBackupContent extends LitElement {
.iconPath=${section === "addons" ? mdiPuzzle : mdiFolder}
.imageUrl=${section === "addons" &&
!this.onboarding &&
this.hass &&
atLeastVersion(this.hass.config.version, 0, 105) &&
addons?.get(item.slug)?.icon
? `/api/hassio/addons/${item.slug}/icon`

View File

@@ -1,4 +1,4 @@
import type { TemplateResult } from "lit";
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../src/components/ha-svg-icon";
@@ -30,27 +30,29 @@ class SupervisorFormfieldLabel extends LitElement {
`;
}
static styles = css`
:host {
display: flex;
align-items: center;
}
.label {
margin-right: 4px;
margin-inline-end: 4px;
margin-inline-start: initial;
}
.version {
color: var(--secondary-text-color);
}
.icon {
max-height: 22px;
max-width: 22px;
margin-right: 8px;
margin-inline-end: 8px;
margin-inline-start: initial;
}
`;
static get styles(): CSSResultGroup {
return css`
:host {
display: flex;
align-items: center;
}
.label {
margin-right: 4px;
margin-inline-end: 4px;
margin-inline-start: initial;
}
.version {
color: var(--secondary-text-color);
}
.icon {
max-height: 22px;
max-width: 22px;
margin-right: 8px;
margin-inline-end: 8px;
margin-inline-start: initial;
}
`;
}
}
declare global {

View File

@@ -1,4 +1,4 @@
import type { TemplateResult } from "lit";
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
@@ -31,41 +31,46 @@ class SupervisorMetric extends LitElement {
</ha-settings-row>`;
}
static styles = css`
ha-settings-row {
padding: 0;
height: 54px;
width: 100%;
}
ha-settings-row > div[slot="description"] {
white-space: normal;
color: var(--secondary-text-color);
display: flex;
justify-content: space-between;
}
ha-bar {
--ha-bar-primary-color: var(--hassio-bar-ok-color, var(--success-color));
}
.target-warning {
--ha-bar-primary-color: var(
--hassio-bar-warning-color,
var(--warning-color)
);
}
.target-critical {
--ha-bar-primary-color: var(
--hassio-bar-critical-color,
var(--error-color)
);
}
.value {
width: 48px;
padding-right: 4px;
padding-inline-start: initial;
padding-inline-end: 4px;
flex-shrink: 0;
}
`;
static get styles(): CSSResultGroup {
return css`
ha-settings-row {
padding: 0;
height: 54px;
width: 100%;
}
ha-settings-row > div[slot="description"] {
white-space: normal;
color: var(--secondary-text-color);
display: flex;
justify-content: space-between;
}
ha-bar {
--ha-bar-primary-color: var(
--hassio-bar-ok-color,
var(--success-color)
);
}
.target-warning {
--ha-bar-primary-color: var(
--hassio-bar-warning-color,
var(--warning-color)
);
}
.target-critical {
--ha-bar-primary-color: var(
--hassio-bar-critical-color,
var(--error-color)
);
}
.value {
width: 48px;
padding-right: 4px;
padding-inline-start: initial;
padding-inline-end: 4px;
flex-shrink: 0;
}
`;
}
}
declare global {

View File

@@ -28,7 +28,7 @@ export class DialogHassioBackupUpload
await this.updateComplete;
}
public closeDialog() {
public closeDialog(): void {
if (this._dialogParams && !this._dialogParams.onboarding) {
if (this._dialogParams.reloadBackup) {
this._dialogParams.reloadBackup();
@@ -36,7 +36,6 @@ export class DialogHassioBackupUpload
}
this._dialogParams = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
return true;
}
protected render() {
@@ -72,7 +71,7 @@ export class DialogHassioBackupUpload
</ha-header-bar>
</div>
<hassio-upload-backup
@hassio-backup-uploaded=${this._backupUploaded}
@backup-uploaded=${this._backupUploaded}
.hass=${this.hass}
></hassio-upload-backup>
</ha-dialog>

View File

@@ -8,11 +8,9 @@ import { atLeastVersion } from "../../../../src/common/config/version";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { stopPropagation } from "../../../../src/common/dom/stop_propagation";
import { slugify } from "../../../../src/common/string/slugify";
import "../../../../src/components/ha-md-dialog";
import "../../../../src/components/ha-dialog-header";
import "../../../../src/components/ha-dialog";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-button-menu";
import "../../../../src/components/ha-header-bar";
import "../../../../src/components/ha-icon-button";
@@ -21,7 +19,6 @@ import type { HassioBackupDetail } from "../../../../src/data/hassio/backup";
import {
fetchHassioBackupInfo,
removeBackup,
restoreBackup,
} from "../../../../src/data/hassio/backup";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import {
@@ -35,14 +32,14 @@ import { fileDownload } from "../../../../src/util/file_download";
import "../../components/supervisor-backup-content";
import type { SupervisorBackupContent } from "../../components/supervisor-backup-content";
import type { HassioBackupDialogParams } from "./show-dialog-hassio-backup";
import type { HaMdDialog } from "../../../../src/components/ha-md-dialog";
import type { BackupOrRestoreKey } from "../../util/translations";
@customElement("dialog-hassio-backup")
class HassioBackupDialog
extends LitElement
implements HassDialog<HassioBackupDialogParams>
{
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public hass?: HomeAssistant;
@state() private _error?: string;
@@ -55,24 +52,13 @@ class HassioBackupDialog
@query("supervisor-backup-content")
private _backupContent!: SupervisorBackupContent;
@query("ha-md-dialog") private _dialog?: HaMdDialog;
public async showDialog(dialogParams: HassioBackupDialogParams) {
this._dialogParams = dialogParams;
this._backup = await fetchHassioBackupInfo(this.hass, dialogParams.slug);
if (!this._backup) {
this._error = this._dialogParams.supervisor?.localize(
"backup.no_backup_found"
);
} else if (this._dialogParams.onboarding && !this._backup.homeassistant) {
this._error = this._dialogParams.supervisor?.localize(
"backup.restore_no_home_assistant"
);
}
this._dialogParams = dialogParams;
this._restoringBackup = false;
}
private _dialogClosed(): void {
public closeDialog() {
this._backup = undefined;
this._dialogParams = undefined;
this._restoringBackup = false;
@@ -80,9 +66,11 @@ class HassioBackupDialog
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
public closeDialog() {
this._dialog?.close();
return true;
private _localize(key: BackupOrRestoreKey) {
return (
this._dialogParams!.supervisor?.localize(`backup.${key}`) ||
this._dialogParams!.localize!(`ui.panel.page-onboarding.restore.${key}`)
);
}
protected render() {
@@ -90,79 +78,100 @@ class HassioBackupDialog
return nothing;
}
return html`
<ha-md-dialog
<ha-dialog
open
.disableCancelAction=${!this._error}
@closed=${this._dialogClosed}
scrimClickAction
@closed=${this.closeDialog}
.heading=${this._backup.name}
>
<ha-dialog-header slot="headline">
<ha-icon-button
slot="navigationIcon"
.label=${this._dialogParams.supervisor?.localize("backup.close")}
.path=${mdiClose}
@click=${this.closeDialog}
.disabled=${this._restoringBackup}
></ha-icon-button>
<span slot="title" .title=${this._backup.name}
>${this._backup.name}</span
>
${!this._dialogParams.onboarding && this._dialogParams.supervisor
? html`<ha-button-menu
slot="actionItems"
fixed
@action=${this._handleMenuAction}
@closed=${stopPropagation}
<div slot="heading">
<ha-header-bar>
<span slot="title">${this._backup.name}</span>
<ha-icon-button
.label=${this._localize("close")}
.path=${mdiClose}
slot="actionItems"
dialogAction="cancel"
></ha-icon-button>
</ha-header-bar>
</div>
${this._restoringBackup
? html`<ha-circular-progress indeterminate></ha-circular-progress>`
: html`
<supervisor-backup-content
.hass=${this.hass}
.supervisor=${this._dialogParams.supervisor}
.backup=${this._backup}
.onboarding=${this._dialogParams.onboarding || false}
.localize=${this._dialogParams.localize}
dialogInitialFocus
>
<ha-icon-button
.label=${this._dialogParams.supervisor.localize(
"backup.more_actions"
)}
.path=${mdiDotsVertical}
slot="trigger"
></ha-icon-button>
<mwc-list-item
>${this._dialogParams.supervisor.localize(
"backup.download_backup"
)}</mwc-list-item
>
<mwc-list-item class="error"
>${this._dialogParams.supervisor.localize(
"backup.delete_backup_title"
)}</mwc-list-item
>
</ha-button-menu>`
: nothing}
</ha-dialog-header>
<div slot="content">
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: this._restoringBackup
? html`<div class="loading">
<ha-circular-progress indeterminate></ha-circular-progress>
</div>`
: html`
<supervisor-backup-content
.hass=${this.hass}
.supervisor=${this._dialogParams.supervisor}
.backup=${this._backup}
.onboarding=${this._dialogParams.onboarding || false}
dialogInitialFocus
>
</supervisor-backup-content>
`}
</div>
<div slot="actions">
<ha-button
.disabled=${this._restoringBackup || !!this._error}
@click=${this._restoreClicked}
>
${this._dialogParams.supervisor?.localize("backup.restore")}
</ha-button>
</div>
</ha-md-dialog>
</supervisor-backup-content>
`}
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: nothing}
<mwc-button
.disabled=${this._restoringBackup}
slot="secondaryAction"
@click=${this._restoreClicked}
>
${this._localize("restore")}
</mwc-button>
${!this._dialogParams.onboarding && this._dialogParams.supervisor
? html`<ha-button-menu
fixed
slot="primaryAction"
@action=${this._handleMenuAction}
@closed=${stopPropagation}
>
<ha-icon-button
.label=${this._dialogParams.supervisor.localize(
"backup.more_actions"
)}
.path=${mdiDotsVertical}
slot="trigger"
></ha-icon-button>
<mwc-list-item
>${this._dialogParams.supervisor.localize(
"backup.download_backup"
)}</mwc-list-item
>
<mwc-list-item class="error"
>${this._dialogParams.supervisor.localize(
"backup.delete_backup_title"
)}</mwc-list-item
>
</ha-button-menu>`
: nothing}
</ha-dialog>
`;
}
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
ha-circular-progress {
display: block;
text-align: center;
}
ha-header-bar {
--mdc-theme-on-primary: var(--primary-text-color);
--mdc-theme-primary: var(--mdc-theme-surface);
flex-shrink: 0;
display: block;
}
ha-icon-button {
color: var(--secondary-text-color);
}
`,
];
}
private _handleMenuAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
@@ -177,7 +186,16 @@ class HassioBackupDialog
private async _restoreClicked() {
const backupDetails = this._backupContent.backupDetails();
this._restoringBackup = true;
this._dialogParams?.onRestoring?.();
if (this._backupContent.backupType === "full") {
await this._fullRestoreClicked(backupDetails);
} else {
await this._partialRestoreClicked(backupDetails);
}
this._restoringBackup = false;
}
private async _partialRestoreClicked(backupDetails) {
const supervisor = this._dialogParams?.supervisor;
if (supervisor !== undefined && supervisor.info.state !== "running") {
await showAlertDialog(this, {
@@ -186,50 +204,91 @@ class HassioBackupDialog
state: supervisor.info.state,
}),
});
this._restoringBackup = false;
return;
}
if (
!(await showConfirmationDialog(this, {
title: supervisor?.localize(
`backup.${
this._backup!.type === "full"
? "confirm_restore_full_backup_title"
: "confirm_restore_partial_backup_title"
}`
),
text: supervisor?.localize(
`backup.${
this._backup!.type === "full"
? "confirm_restore_full_backup_text"
: "confirm_restore_partial_backup_text"
}`
),
confirmText: supervisor?.localize("backup.restore"),
dismissText: supervisor?.localize("backup.cancel"),
title: this._localize("confirm_restore_partial_backup_title"),
text: this._localize("confirm_restore_partial_backup_text"),
confirmText: this._localize("restore"),
dismissText: this._localize("cancel"),
}))
) {
this._restoringBackup = false;
return;
}
try {
await restoreBackup(
this.hass,
this._backup!.type,
this._backup!.slug,
{ ...backupDetails, background: this._dialogParams?.onboarding },
!!this.hass && atLeastVersion(this.hass.config.version, 2021, 9)
);
if (!this._dialogParams?.onboarding) {
try {
await this.hass!.callApi(
"POST",
`hassio/${
atLeastVersion(this.hass!.config.version, 2021, 9)
? "backups"
: "snapshots"
}/${this._backup!.slug}/restore/partial`,
backupDetails
);
this.closeDialog();
} catch (error: any) {
this._error = error.body.message;
}
} else {
this._dialogParams?.onRestoring?.();
await fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
method: "POST",
body: JSON.stringify(backupDetails),
});
this.closeDialog();
}
}
private async _fullRestoreClicked(backupDetails) {
const supervisor = this._dialogParams?.supervisor;
if (supervisor !== undefined && supervisor.info.state !== "running") {
await showAlertDialog(this, {
title: supervisor.localize("backup.could_not_restore"),
text: supervisor.localize("backup.restore_blocked_not_running", {
state: supervisor.info.state,
}),
});
return;
}
if (
!(await showConfirmationDialog(this, {
title: this._localize("confirm_restore_full_backup_title"),
text: this._localize("confirm_restore_full_backup_text"),
confirmText: this._localize("restore"),
dismissText: this._localize("cancel"),
}))
) {
return;
}
if (!this._dialogParams?.onboarding) {
this.hass!.callApi(
"POST",
`hassio/${
atLeastVersion(this.hass!.config.version, 2021, 9)
? "backups"
: "snapshots"
}/${this._backup!.slug}/restore/full`,
backupDetails
).then(
() => {
this.closeDialog();
},
(error) => {
this._error = error.body.message;
}
);
} else {
this._dialogParams?.onRestoring?.();
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/full`, {
method: "POST",
body: JSON.stringify(backupDetails),
});
this.closeDialog();
} catch (error: any) {
this._error =
error?.body?.message ||
supervisor?.localize("backup.restore_start_failed");
} finally {
this._restoringBackup = false;
}
}
@@ -286,7 +345,7 @@ class HassioBackupDialog
title: supervisor.localize("backup.remote_download_title"),
text: supervisor.localize("backup.remote_download_text"),
confirmText: supervisor.localize("backup.download"),
dismissText: supervisor?.localize("backup.cancel"),
dismissText: this._localize("cancel"),
});
if (!confirm) {
return;
@@ -302,36 +361,7 @@ class HassioBackupDialog
private get _computeName() {
return this._backup
? this._backup.name || this._backup.slug
: this._dialogParams!.supervisor?.localize("backup.unnamed_backup") || "";
}
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
ha-circular-progress {
display: block;
text-align: center;
}
ha-header-bar {
--mdc-theme-on-primary: var(--primary-text-color);
--mdc-theme-primary: var(--mdc-theme-surface);
flex-shrink: 0;
display: block;
}
ha-icon-button {
color: var(--secondary-text-color);
}
.loading {
width: 100%;
display: flex;
height: 100%;
justify-content: center;
align-items: center;
}
`,
];
: "Unnamed backup";
}
}

View File

@@ -1,4 +1,5 @@
import { fireEvent } from "../../../../src/common/dom/fire_event";
import type { LocalizeFunc } from "../../../../src/common/translations/localize";
import type { Supervisor } from "../../../../src/data/supervisor/supervisor";
export interface HassioBackupDialogParams {
@@ -7,6 +8,7 @@ export interface HassioBackupDialogParams {
onRestoring?: () => void;
onboarding?: boolean;
supervisor?: Supervisor;
localize?: LocalizeFunc;
}
export const showHassioBackupDialog = (

View File

@@ -82,11 +82,10 @@ export class DialogHassioNetwork
await this.updateComplete;
}
public closeDialog() {
public closeDialog(): void {
this._params = undefined;
this._processing = false;
fireEvent(this, "dialog-closed", { dialog: this.localName });
return true;
}
protected render() {

View File

@@ -92,7 +92,7 @@ class HassioMyRedirect extends LitElement {
let url: string;
try {
url = this._createRedirectUrl(redirect);
} catch (_err: any) {
} catch (err: any) {
this._error = this.supervisor.localize("my.error");
return;
}

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