Compare commits

..

2 Commits

Author SHA1 Message Date
J. Nick Koston
dd00b51f21 Adjust WebSocket ping timeout to 15 seconds
5 seconds was too low to prevent the UI from reloading
when connecting the WebSocket during startup or on
a high latancy connection

This problem presented as the UI reloading over
and over again because it could never respond
to the ping in time on high latancy connections.

At startup it usually only did this once so it
went unnoticed in most cases.

This ping was added in #18934
2025-02-20 11:46:59 -06:00
J. Nick Koston
64b886eea0 Reduce size of address column on Bluetooth Advertisement monitor 2025-01-29 12:51:56 -06:00
995 changed files with 19949 additions and 42167 deletions

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": [
@@ -21,8 +18,7 @@
"esbenp.prettier-vscode",
"runem.lit-plugin",
"github.vscode-pull-request-github",
"eamodio.gitlens",
"yeion7.styled-global-variables-autocomplete"
"eamodio.gitlens"
],
"settings": {
"files.eol": "\n",

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

@@ -26,7 +26,7 @@ jobs:
ref: dev
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v4.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -41,8 +41,9 @@ jobs:
- name: Deploy to Netlify
id: deploy
run: |
npx -y netlify-cli deploy --dir=cast/dist --alias dev
uses: netlify/actions/cli@master
with:
args: deploy --dir=cast/dist --alias dev
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_CAST_SITE_ID }}
@@ -61,7 +62,7 @@ jobs:
ref: master
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v4.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -76,8 +77,9 @@ jobs:
- name: Deploy to Netlify
id: deploy
run: |
npx -y netlify-cli deploy --dir=cast/dist --prod
uses: netlify/actions/cli@master
with:
args: deploy --dir=cast/dist --prod
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_CAST_SITE_ID }}

View File

@@ -26,7 +26,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v4.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -37,7 +37,7 @@ jobs:
- 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.3
uses: actions/cache@v4.2.0
with:
path: |
node_modules/.cache/prettier
@@ -60,7 +60,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v4.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -78,7 +78,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v4.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -89,7 +89,7 @@ jobs:
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@v4.6.2
uses: actions/upload-artifact@v4.6.0
with:
name: frontend-bundle-stats
path: build/stats/*.json
@@ -102,7 +102,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v4.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -113,7 +113,7 @@ jobs:
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@v4.6.2
uses: actions/upload-artifact@v4.6.0
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.4.0
uses: actions/setup-node@v4.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -42,8 +42,9 @@ jobs:
- name: Deploy to Netlify
id: deploy
run: |
npx -y netlify-cli deploy --dir=demo/dist --prod
uses: netlify/actions/cli@master
with:
args: deploy --dir=demo/dist --prod
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_DEV_SITE_ID }}
@@ -62,7 +63,7 @@ jobs:
ref: master
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v4.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -77,8 +78,9 @@ jobs:
- name: Deploy to Netlify
id: deploy
run: |
npx -y netlify-cli deploy --dir=demo/dist --prod
uses: netlify/actions/cli@master
with:
args: deploy --dir=demo/dist --prod
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_SITE_ID }}

View File

@@ -19,7 +19,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v4.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -34,8 +34,9 @@ jobs:
- name: Deploy to Netlify
id: deploy
run: |
npx -y netlify-cli deploy --dir=gallery/dist --prod
uses: netlify/actions/cli@master
with:
args: deploy --dir=gallery/dist --prod
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_GALLERY_SITE_ID }}

View File

@@ -24,7 +24,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v4.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -39,14 +39,13 @@ jobs:
- name: Deploy preview to Netlify
id: deploy
run: |
npx -y netlify-cli deploy --dir=gallery/dist --alias "deploy-preview-${{ github.event.number }}" \
--json > deploy_output.json
uses: netlify/actions/cli@master
with:
args: deploy --dir=gallery/dist --alias "deploy-preview-${{ github.event.number }}"
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_GALLERY_SITE_ID }}
- name: Generate summary
run: |
NETLIFY_LIVE_URL=$(jq -r '.deploy_url' deploy_output.json)
echo "$NETLIFY_LIVE_URL" >> "$GITHUB_STEP_SUMMARY"
echo "${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}" >> "$GITHUB_STEP_SUMMARY"

View File

@@ -28,7 +28,7 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v4.2.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.2
uses: actions/upload-artifact@v4.6.0
with:
name: wheels
path: dist/home_assistant_frontend*.whl
if-no-files-found: error
- name: Upload translations
uses: actions/upload-artifact@v4.6.2
uses: actions/upload-artifact@v4.6.0
with:
name: translations
path: translations.tar.gz

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send bundle stats and build information to RelativeCI
uses: relative-ci/agent-action@v2.2.0
uses: relative-ci/agent-action@v2.1.14
with:
key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
token: ${{ github.token }}

View File

@@ -34,7 +34,7 @@ jobs:
uses: home-assistant/actions/helpers/verify-version@master
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v4.2.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.2
uses: softprops/action-gh-release@v2.2.1
with:
files: |
dist/*.whl
@@ -74,7 +74,7 @@ jobs:
echo "home-assistant-frontend==$version" > ./requirements.txt
- name: Build wheels
uses: home-assistant/wheels@2025.03.0
uses: home-assistant/wheels@2024.11.0
with:
abi: cp313
tag: musllinux_1_2
@@ -92,7 +92,7 @@ jobs:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v4.2.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.2
uses: softprops/action-gh-release@v2.2.1
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.4.0
uses: actions/setup-node@v4.2.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.2
uses: softprops/action-gh-release@v2.2.1
with:
files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz

View File

@@ -5,7 +5,6 @@
"runem.lit-plugin",
"github.vscode-pull-request-github",
"eamodio.gitlens",
"vitest.explorer",
"yeion7.styled-global-variables-autocomplete"
"vitest.explorer"
]
}

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"
}
]
}

View File

@@ -1,22 +0,0 @@
diff --git a/mwc-formfield-base.js b/mwc-formfield-base.js
index 7b763326d7d51835ad52646bfbc80fe21989abd3..f2baa8224e6d03df1fdb0b9fd03f5c6d77fc8747 100644
--- a/mwc-formfield-base.js
+++ b/mwc-formfield-base.js
@@ -9,7 +9,7 @@ import { BaseElement } from '@material/mwc-base/base-element.js';
import { FormElement } from '@material/mwc-base/form-element.js';
import { observer } from '@material/mwc-base/observer.js';
import { html } from 'lit';
-import { property, query, queryAssignedNodes } from 'lit/decorators.js';
+import { property, query, queryAssignedElements } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
export class FormfieldBase extends BaseElement {
constructor() {
@@ -96,7 +96,7 @@ __decorate([
query('.mdc-form-field')
], FormfieldBase.prototype, "mdcRoot", void 0);
__decorate([
- queryAssignedNodes('', true, '*')
+ queryAssignedElements({ slot: "", flatten: true, selector: "*" })
], FormfieldBase.prototype, "slottedInputs", void 0);
__decorate([
query('label')

View File

@@ -1,26 +0,0 @@
diff --git a/mwc-list-base.js b/mwc-list-base.js
index 1ba95b6a01dcecea4d85b5cbbbcc3dfb04c40d5f..dced13fdb7929c490d6661b1bbe7e9f96dcd2285 100644
--- a/mwc-list-base.js
+++ b/mwc-list-base.js
@@ -11,7 +11,7 @@ import { BaseElement } from '@material/mwc-base/base-element.js';
import { observer } from '@material/mwc-base/observer.js';
import { deepActiveElementPath, doesElementContainFocus, isNodeElement } from '@material/mwc-base/utils.js';
import { html } from 'lit';
-import { property, query, queryAssignedNodes } from 'lit/decorators.js';
+import { property, query, queryAssignedElements } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import MDCListFoundation, { isIndexSet } from './mwc-list-foundation.js';
export { createSetFromIndex, isEventMulti, isIndexSet } from './mwc-list-foundation.js';
@@ -425,10 +425,10 @@ __decorate([
query('.mdc-deprecated-list')
], ListBase.prototype, "mdcRoot", void 0);
__decorate([
- queryAssignedNodes('', true, '*')
+ queryAssignedElements({ flatten: true, selector: "*" })
], ListBase.prototype, "assignedElements", void 0);
__decorate([
- queryAssignedNodes('', true, '[tabindex="0"]')
+ queryAssignedElements({ flatten: true, selector: '[tabindex="0"]' })
], ListBase.prototype, "tabbableElements", void 0);
__decorate([
property({ type: Boolean }),

View File

@@ -0,0 +1,34 @@
diff --git a/lib/legacy/class.js b/lib/legacy/class.js
index aee2511be1cd9bf900ee552bc98190c1631c57c0..f2f499d68bf52034cac9c28307c99e8ce6b8417d 100644
--- a/lib/legacy/class.js
+++ b/lib/legacy/class.js
@@ -304,17 +304,23 @@ function GenerateClassFromInfo(info, Base, behaviors) {
// only proceed if the generated class' prototype has not been registered.
const generatedProto = PolymerGenerated.prototype;
if (!generatedProto.hasOwnProperty(JSCompiler_renameProperty('__hasRegisterFinished', generatedProto))) {
- generatedProto.__hasRegisterFinished = true;
+ // make sure legacy lifecycle is called on the *element*'s prototype
+ // and not the generated class prototype; if the element has been
+ // extended, these are *not* the same.
+ const proto = Object.getPrototypeOf(this);
+ // Only set flag when generated prototype itself is registered,
+ // as this element may be extended from, and needs to run `registered`
+ // on all behaviors on the subclass as well.
+ if (proto === generatedProto) {
+ generatedProto.__hasRegisterFinished = true;
+ }
// ensure superclass is registered first.
super._registered();
// copy properties onto the generated class lazily if we're optimizing,
- if (legacyOptimizations) {
+ if (legacyOptimizations && !Object.hasOwnProperty(generatedProto, '__hasCopiedProperties')) {
+ generatedProto.__hasCopiedProperties = true;
copyPropertiesToProto(generatedProto);
}
- // make sure legacy lifecycle is called on the *element*'s prototype
- // and not the generated class prototype; if the element has been
- // extended, these are *not* the same.
- const proto = Object.getPrototypeOf(this);
let list = lifecycle.beforeRegister;
if (list) {
for (let i=0; i < list.length; i++) {

934
.yarn/releases/yarn-4.6.0.cjs vendored Executable file

File diff suppressed because one or more lines are too long

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.9.1.cjs
yarnPath: .yarn/releases/yarn-4.6.0.cjs

View File

@@ -2,7 +2,7 @@ import defineProvider from "@babel/helper-define-polyfill-provider";
import { join } from "node:path";
import paths from "../paths.cjs";
const POLYFILL_DIR = join(paths.root_dir, "src/resources/polyfills");
const POLYFILL_DIR = join(paths.polymer_dir, "src/resources/polyfills");
// List of polyfill keys with supported browser targets for the functionality
const polyfillSupport = {

View File

@@ -18,18 +18,30 @@ module.exports.sourceMapURL = () => {
module.exports.ignorePackages = () => [];
// Files from NPM packages that we should replace with empty file
module.exports.emptyPackages = ({ isHassioBuild }) =>
module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
[
// Contains all color definitions for all material color sets.
// We don't use it
require.resolve("@polymer/paper-styles/color.js"),
require.resolve("@polymer/paper-styles/default-theme.js"),
// Loads stuff from a CDN
require.resolve("@polymer/font-roboto/roboto.js"),
require.resolve("@vaadin/vaadin-material-styles/typography.js"),
require.resolve("@vaadin/vaadin-material-styles/font-icons.js"),
// Compatibility not needed for latest builds
latestBuild &&
// wrapped in require.resolve so it blows up if file no longer exists
require.resolve(
path.resolve(paths.polymer_dir, "src/resources/compatibility.ts")
),
// Icons in supervisor conflict with icons in HA so we don't load.
isHassioBuild &&
require.resolve(
path.resolve(paths.root_dir, "src/components/ha-icon.ts")
path.resolve(paths.polymer_dir, "src/components/ha-icon.ts")
),
isHassioBuild &&
require.resolve(
path.resolve(paths.root_dir, "src/components/ha-icon-picker.ts")
path.resolve(paths.polymer_dir, "src/components/ha-icon-picker.ts")
),
].filter(Boolean);
@@ -43,9 +55,8 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
__STATIC_PATH__: "/static/",
__HASS_URL__: `\`${
"HASS_URL" in process.env
? process.env.HASS_URL
: // eslint-disable-next-line no-template-curly-in-string
"${location.protocol}//${location.host}"
? process.env["HASS_URL"]
: "${location.protocol}//${location.host}"
}\``,
"process.env.NODE_ENV": JSON.stringify(
isProdBuild ? "production" : "development"
@@ -73,19 +84,6 @@ module.exports.terserOptions = ({ latestBuild, isTestBuild }) => ({
sourceMap: !isTestBuild,
});
/** @type {import('@rspack/core').SwcLoaderOptions} */
module.exports.swcOptions = () => ({
jsc: {
loose: true,
externalHelpers: true,
target: "ES2021",
parser: {
syntax: "typescript",
decorators: true,
},
},
});
module.exports.babelOptions = ({
latestBuild,
isProdBuild,
@@ -110,6 +108,7 @@ module.exports.babelOptions = ({
shippedProposals: true,
},
],
"@babel/preset-typescript",
],
plugins: [
[
@@ -146,6 +145,12 @@ module.exports.babelOptions = ({
"@babel/plugin-transform-runtime",
{ version: dependencies["@babel/runtime"] },
],
// Transpile decorators (still in TC39 process)
// Modern browsers support class fields and private methods, but transform is required with the older decorator version dictated by Lit
[
"@babel/plugin-proposal-decorators",
{ version: "2018-09", decoratorsBeforeExport: true },
],
"@babel/plugin-transform-class-properties",
"@babel/plugin-transform-private-methods",
].filter(Boolean),
@@ -165,7 +170,7 @@ module.exports.babelOptions = ({
],
],
exclude: [
path.join(paths.root_dir, "src/resources/polyfills"),
path.join(paths.polymer_dir, "src/resources/polyfills"),
...[
"@formatjs/(?:ecma402-abstract|intl-\\w+)",
"@lit-labs/virtualizer/polyfills",
@@ -183,7 +188,6 @@ module.exports.babelOptions = ({
include: /\/node_modules\//,
exclude: [
"element-internals-polyfill",
"@shoelace-style",
"@?lit(?:-labs|-element|-html)?",
].map((p) => new RegExp(`/node_modules/${p}/`)),
},

View File

@@ -21,7 +21,7 @@ module.exports = {
},
version() {
const version = fs
.readFileSync(path.resolve(paths.root_dir, "pyproject.toml"), "utf8")
.readFileSync(path.resolve(paths.polymer_dir, "pyproject.toml"), "utf8")
.match(/version\W+=\W"(\d{8}\.\d(?:\.dev)?)"/);
if (!version) {
throw Error("Version not found");

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-require-imports": "off",
"prefer-arrow-callback": "off",
},
},
});
];

View File

@@ -56,7 +56,6 @@ const getCommonTemplateVars = () => {
);
return {
modernRegex: compileRegex(browserRegexes.concat(haMacOSRegex)).toString(),
hassUrl: process.env.HASS_URL || "",
};
};
@@ -169,14 +168,14 @@ const APP_PAGE_ENTRIES = {
gulp.task(
"gen-pages-app-dev",
genPagesDevTask(APP_PAGE_ENTRIES, paths.root_dir, paths.app_output_root)
genPagesDevTask(APP_PAGE_ENTRIES, paths.polymer_dir, paths.app_output_root)
);
gulp.task(
"gen-pages-app-prod",
genPagesProdTask(
APP_PAGE_ENTRIES,
paths.root_dir,
paths.polymer_dir,
paths.app_output_root,
paths.app_output_latest,
paths.app_output_es5

View File

@@ -6,8 +6,8 @@ import path from "path";
import paths from "../paths.cjs";
const npmPath = (...parts) =>
path.resolve(paths.root_dir, "node_modules", ...parts);
const polyPath = (...parts) => path.resolve(paths.root_dir, ...parts);
path.resolve(paths.polymer_dir, "node_modules", ...parts);
const polyPath = (...parts) => path.resolve(paths.polymer_dir, ...parts);
const copyFileDir = (fromFile, toDir) =>
fs.copySync(fromFile, path.join(toDir, path.basename(fromFile)));
@@ -59,11 +59,6 @@ function copyPolyfills(staticDir) {
npmPath("@webcomponents/webcomponentsjs/webcomponents-bundle.js.map"),
staticPath("polyfills/")
);
// Lit polyfill support
fs.copySync(
npmPath("lit/polyfill-support.js"),
path.join(staticPath("polyfills/"), "lit-polyfill-support.js")
);
// dialog-polyfill css
copyFileDir(
@@ -95,10 +90,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/")

View File

@@ -4,7 +4,7 @@ import gulp from "gulp";
import { join, resolve } from "node:path";
import paths from "../paths.cjs";
const formatjsDir = join(paths.root_dir, "node_modules", "@formatjs");
const formatjsDir = join(paths.polymer_dir, "node_modules", "@formatjs");
const outDir = join(paths.build_dir, "locale-data");
const INTL_POLYFILLS = {

View File

@@ -40,17 +40,20 @@ class CustomJSON extends Transform {
this._reviver = reviver;
}
// eslint-disable-next-line @typescript-eslint/naming-convention
async _transform(file, _, callback) {
let obj = JSON.parse(file.contents.toString(), this._reviver);
if (this._func) obj = this._func(obj, file.path);
for (const [outObj, dir] of Array.isArray(obj) ? obj : [[obj, ""]]) {
const outFile = file.clone({ contents: false });
outFile.contents = Buffer.from(JSON.stringify(outObj));
outFile.dirname += `/${dir}`;
this.push(outFile);
try {
let obj = JSON.parse(file.contents.toString(), this._reviver);
if (this._func) obj = this._func(obj, file.path);
for (const [outObj, dir] of Array.isArray(obj) ? obj : [[obj, ""]]) {
const outFile = file.clone({ contents: false });
outFile.contents = Buffer.from(JSON.stringify(outObj));
outFile.dirname += `/${dir}`;
this.push(outFile);
}
callback(null);
} catch (err) {
callback(err);
}
callback(null);
}
}
@@ -65,19 +68,25 @@ class MergeJSON extends Transform {
this._reviver = reviver;
}
// eslint-disable-next-line @typescript-eslint/naming-convention
async _transform(file, _, callback) {
this._objects.push(JSON.parse(file.contents.toString(), this._reviver));
if (!this._outFile) this._outFile = file.clone({ contents: false });
callback(null);
try {
this._objects.push(JSON.parse(file.contents.toString(), this._reviver));
if (!this._outFile) this._outFile = file.clone({ contents: false });
callback(null);
} catch (err) {
callback(err);
}
}
// eslint-disable-next-line @typescript-eslint/naming-convention
async _flush(callback) {
const mergedObj = merge(this._startObj, ...this._objects);
this._outFile.contents = Buffer.from(JSON.stringify(mergedObj));
this._outFile.stem = this._stem;
callback(null, this._outFile);
try {
const mergedObj = merge(this._startObj, ...this._objects);
this._outFile.contents = Buffer.from(JSON.stringify(mergedObj));
this._outFile.stem = this._stem;
callback(null, this._outFile);
} catch (err) {
callback(err);
}
}
}

View File

@@ -16,7 +16,6 @@ const detailsClose = "</details>\n";
const dummyAPI = {
version: babelVersion,
// eslint-disable-next-line @typescript-eslint/no-empty-function
assertVersion: () => {},
caller: (callback) =>
callback({

View File

@@ -1,7 +1,7 @@
const path = require("path");
module.exports = {
root_dir: path.resolve(__dirname, ".."),
polymer_dir: path.resolve(__dirname, ".."),
build_dir: path.resolve(__dirname, "../build"),
app_output_root: path.resolve(__dirname, "../hass_frontend"),

View File

@@ -1,17 +1,12 @@
const { existsSync } = require("fs");
const path = require("path");
const rspack = require("@rspack/core");
// eslint-disable-next-line @typescript-eslint/naming-convention
const { RsdoctorRspackPlugin } = require("@rsdoctor/rspack-plugin");
// eslint-disable-next-line @typescript-eslint/naming-convention
const { StatsWriterPlugin } = require("webpack-stats-plugin");
const filterStats = require("@bundle-stats/plugin-webpack-filter");
// eslint-disable-next-line @typescript-eslint/naming-convention
const filterStats = require("@bundle-stats/plugin-webpack-filter").default;
const TerserPlugin = require("terser-webpack-plugin");
// eslint-disable-next-line @typescript-eslint/naming-convention
const { WebpackManifestPlugin } = require("rspack-manifest-plugin");
const log = require("fancy-log");
// eslint-disable-next-line @typescript-eslint/naming-convention
const WebpackBar = require("webpackbar/rspack");
const paths = require("./paths.cjs");
const bundle = require("./bundle.cjs");
@@ -65,26 +60,19 @@ const createRspackConfig = ({
rules: [
{
test: /\.m?js$|\.ts$/,
exclude: /node_modules[\\/]core-js/,
use: (info) => [
{
loader: "babel-loader",
options: {
...bundle.babelOptions({
latestBuild,
isProdBuild,
isTestBuild,
sw: info.issuerLayer === "sw",
}),
cacheDirectory: !isProdBuild,
cacheCompression: false,
},
use: (info) => ({
loader: "babel-loader",
options: {
...bundle.babelOptions({
latestBuild,
isProdBuild,
isTestBuild,
sw: info.issuerLayer === "sw",
}),
cacheDirectory: !isProdBuild,
cacheCompression: false,
},
{
loader: "builtin:swc-loader",
options: bundle.swcOptions(),
},
],
}),
resolve: {
fullySpecified: false,
},
@@ -143,8 +131,7 @@ const createRspackConfig = ({
// calling define.amd will call require("!!webpack amd options")
resource.startsWith("!!webpack") ||
// loaded by webpack dev server but doesn't exist.
resource === "webpack/hot" ||
resource.startsWith("@swc/helpers")
resource === "webpack/hot"
) {
return false;
}
@@ -168,8 +155,10 @@ const createRspackConfig = ({
},
}),
new rspack.NormalModuleReplacementPlugin(
new RegExp(bundle.emptyPackages({ isHassioBuild }).join("|")),
path.resolve(paths.root_dir, "src/util/empty.js")
new RegExp(
bundle.emptyPackages({ latestBuild, isHassioBuild }).join("|")
),
path.resolve(paths.polymer_dir, "src/util/empty.js")
),
!isProdBuild && new LogStartCompilePlugin(),
isProdBuild &&
@@ -203,7 +192,6 @@ const createRspackConfig = ({
"lit/directives/if-defined$": "lit/directives/if-defined.js",
"lit/directives/guard$": "lit/directives/guard.js",
"lit/directives/cache$": "lit/directives/cache.js",
"lit/directives/join$": "lit/directives/join.js",
"lit/directives/repeat$": "lit/directives/repeat.js",
"lit/directives/live$": "lit/directives/live.js",
"lit/directives/keyed$": "lit/directives/keyed.js",

View File

@@ -1,3 +1,3 @@
import "./layout/hc-connect";
import("../../../src/resources/append-ha-style");
import("../../../src/resources/ha-style");

View File

@@ -1,5 +1,5 @@
import "@material/mwc-button/mwc-button";
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";
@@ -19,8 +19,6 @@ import {
import { atLeastVersion } from "../../../../src/common/config/version";
import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute";
import "../../../../src/components/ha-icon";
import "../../../../src/components/ha-list";
import "../../../../src/components/ha-list-item";
import "../../../../src/components/ha-svg-icon";
import {
getLegacyLovelaceCollection,
@@ -31,6 +29,7 @@ import type { LovelaceViewConfig } from "../../../../src/data/lovelace/config/vi
import "../../../../src/layouts/hass-loading-screen";
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
import "./hc-layout";
import "../../../../src/components/ha-list-item";
@customElement("hc-cast")
class HcCast extends LitElement {
@@ -86,7 +85,7 @@ class HcCast extends LitElement {
`
: html`
<div class="section-header">PICK A VIEW</div>
<ha-list @action=${this._handlePickView} activatable>
<mwc-list @action=${this._handlePickView} activatable>
${(
this.lovelaceViews ?? [
generateDefaultViewConfig({}, {}, {}, {}, () => ""),
@@ -114,7 +113,7 @@ class HcCast extends LitElement {
></ha-svg-icon>`}
</ha-list-item>
`
)}</ha-list
)}</mwc-list
>
`}

View File

@@ -109,7 +109,7 @@ export class HcMain extends HassElement {
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
import("./hc-lovelace");
import("../../../../src/resources/append-ha-style");
import("../../../../src/resources/ha-style");
window.addEventListener("location-changed", () => {
const panelPath = `/${this._urlPath || "lovelace"}/`;
@@ -309,7 +309,7 @@ export class HcMain extends HassElement {
"../../../../src/panels/lovelace/strategies/get-strategy"
);
const config = await generateLovelaceDashboardStrategy(
rawConfig,
rawConfig.strategy,
this.hass!
);
this._handleNewLovelaceConfig(config);
@@ -351,7 +351,10 @@ export class HcMain extends HassElement {
"../../../../src/panels/lovelace/strategies/get-strategy"
);
this._handleNewLovelaceConfig(
await generateLovelaceDashboardStrategy(DEFAULT_CONFIG, this.hass!)
await generateLovelaceDashboardStrategy(
DEFAULT_CONFIG.strategy,
this.hass!
)
);
}

View File

@@ -1,28 +1,37 @@
export const demoThemeJimpower = () => ({
"text-primary-color": "var(--primary-text-color)",
"paper-item-icon-color": "var(--primary-text-color)",
"primary-color": "#5294E2",
"label-badge-red": "var(--accent-color)",
"paper-tabs-selection-bar-color": "green",
"light-primary-color": "var(--accent-color)",
"primary-background-color": "#383C45",
"primary-text-color": "#FFFFFF",
"paper-item-selected_-_background-color": "#434954",
"secondary-background-color": "#383C45",
"disabled-text-color": "#7F848E",
"paper-item-icon_-_color": "green",
"paper-grey-200": "#414A59",
"label-badge-background-color": "#2E333A",
"sidebar-icon-color": "var(--state-icon-color)",
"paper-card-header-color": "var(--accent-color)",
"sidebar-icon-color": "var(--paper-item-icon-color)",
"paper-listbox-background-color": "#2E333A",
"table-row-background-color": "#353840",
"paper-grey-50": "var(--primary-text-color)",
"switch-checked-color": "var(--accent-color)",
"paper-dialog-background-color": "#434954",
"secondary-text-color": "#5294E2",
"error-color": "#E45E65",
"divider-color": "rgba(0, 0, 0, .12)",
"success-color": "#39E949",
"switch-unchecked-button-color": "var(--disabled-text-color)",
"label-badge-border-color": "green",
"paper-listbox-color": "var(--primary-color)",
"card-background-color": "#434954",
"label-badge-text-color": "var(--primary-text-color)",
"switch-unchecked-track-color": "var(--disabled-text-color)",
"dark-primary-color": "var(--accent-color)",
"paper-item-icon-active-color": "#F9C536",
"accent-color": "#E45E65",
"table-row-alternative-background-color": "#3E424B",
});

View File

@@ -1,29 +1,38 @@
// https://community.home-assistant.io/t/slate-a-new-dark-theme/86410
export const demoThemeKernehed = () => ({
"text-primary-color": "var(--primary-text-color)",
"paper-item-icon-color": "var(--primary-text-color)",
"primary-color": "#2980b9",
"label-badge-red": "var(--accent-color)",
"paper-tabs-selection-bar-color": "green",
"primary-text-color": "#FFFFFF",
"light-primary-color": "var(--accent-color)",
"primary-background-color": "#222222",
"sidebar-icon-color": "#777777",
"paper-item-selected_-_background-color": "#292929",
"secondary-background-color": "#222222",
"disabled-text-color": "#777777",
"paper-item-icon_-_color": "green",
"paper-grey-200": "#222222",
"label-badge-background-color": "#222222",
"paper-card-header-color": "var(--accent-color)",
"paper-listbox-background-color": "#141414",
"table-row-background-color": "#292929",
"paper-grey-50": "var(--primary-text-color)",
"switch-checked-color": "var(--accent-color)",
"paper-dialog-background-color": "#292929",
"secondary-text-color": "#b58e31",
"error-color": "#b58e31",
"divider-color": "rgba(0, 0, 0, .12)",
"success-color": "#2980b9",
"switch-unchecked-button-color": "var(--disabled-text-color)",
"label-badge-border-color": "green",
"paper-listbox-color": "#777777",
"card-background-color": "#292929",
"label-badge-text-color": "var(--primary-text-color)",
"switch-unchecked-track-color": "var(--disabled-text-color)",
"dark-primary-color": "var(--accent-color)",
"paper-item-icon-active-color": "#b58e31",
"accent-color": "#2980b9",
"table-row-alternative-background-color": "#292929",
});

View File

@@ -1,18 +1,26 @@
export const demoThemeTeachingbirds = () => ({
"paper-card-header-color": "var(--paper-item-icon-color)",
"paper-listbox-background-color": "#202020",
"paper-grey-50": "var(--primary-text-color)",
"paper-item-icon-color": "#d3d3d3",
"divider-color": "rgba(255, 255, 255, 0.12)",
"primary-color": "#389638",
"light-primary-color": "#6f956f",
"label-badge-red": "var(--primary-color)",
"paper-listbox-color": "#FFFFFF",
"paper-toggle-button-checked-bar-color": "var(--light-primary-color)",
"switch-unchecked-track-color": "var(--primary-text-color)",
"card-background-color": "#4e4e4e",
"label-badge-text-color": "var(--text-primary-color)",
"primary-background-color": "#303030",
"sidebar-icon-color": "#d3d3d3",
"sidebar-icon-color": "var(--paper-item-icon-color)",
"secondary-background-color": "#2b2b2b",
"paper-item-icon-active-color": "#d8bf50",
"switch-checked-color": "var(--primary-color)",
"secondary-text-color": "#389638",
"disabled-text-color": "#545454",
"paper-item-icon_-_color": "var(--primary-text-color)",
"paper-grey-200": "#191919",
"primary-text-color": "#cfcfcf",
"label-badge-background-color": "var(--secondary-background-color)",
});

View File

@@ -73,7 +73,7 @@ class CastDemoRow extends LitElement implements LovelaceRow {
}
ha-svg-icon {
padding: 8px;
color: var(--state-icon-color);
color: var(--paper-item-icon-color);
}
.flex {
flex: 1;

View File

@@ -5,7 +5,7 @@ import { until } from "lit/directives/until";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/ha-card";
import "../../../src/components/ha-button";
import "../../../src/components/ha-spinner";
import "../../../src/components/ha-circular-progress";
import type { LovelaceCardConfig } from "../../../src/data/lovelace/config/card";
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
import type {
@@ -44,7 +44,9 @@ export class HADemoCard extends LitElement implements LovelaceCard {
<div class="picker">
<div class="label">
${this._switching
? html`<ha-spinner></ha-spinner>`
? html`
<ha-circular-progress indeterminate></ha-circular-progress>
`
: until(
selectedDemoConfig.then(
(conf) => html`

View File

@@ -1,4 +1,4 @@
import "./util/is_frontpage";
import "./ha-demo";
import("../../src/resources/append-ha-style");
import("../../src/resources/ha-style");

View File

@@ -1,3 +1,5 @@
// Compat needs to be first import
import "../../src/resources/compatibility";
import { customElement } from "lit/decorators";
import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
import { navigate } from "../../src/common/navigate";
@@ -63,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,
@@ -84,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

@@ -1,10 +1,9 @@
import type { validateConfig } from "../../../src/data/config";
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockConfig = (hass: MockHomeAssistant) => {
hass.mockWS<typeof validateConfig>("validate_config", () => ({
actions: { valid: true, error: null },
conditions: { valid: true, error: null },
triggers: { valid: true, error: null },
hass.mockWS("validate_config", () => ({
actions: { valid: true },
conditions: { valid: true },
triggers: { valid: true },
}));
};

View File

@@ -1,26 +1,19 @@
import type { getConfigEntries } from "../../../src/data/config_entries";
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockConfigEntries = (hass: MockHomeAssistant) => {
hass.mockWS<typeof getConfigEntries>("config_entries/get", () => [
{
entry_id: "mock-entry-co2signal",
domain: "co2signal",
title: "Electricity Maps",
source: "user",
state: "loaded",
supports_options: false,
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,
reason: null,
num_subentries: 0,
error_reason_translation_key: null,
error_reason_translation_placeholders: null,
},
]);
hass.mockWS("config_entries/get", () => ({
entry_id: "co2signal",
domain: "co2signal",
title: "Electricity Maps",
source: "user",
state: "loaded",
supports_options: false,
supports_remove_device: false,
supports_unload: true,
supports_reconfigure: true,
pref_disable_new_entities: false,
pref_disable_polling: false,
disabled_by: null,
reason: null,
}));
};

View File

@@ -2,36 +2,26 @@ import type { TodoItem } from "../../../src/data/todo";
import { TodoItemStatus } from "../../../src/data/todo";
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
const items = {
items: [
{
uid: "12",
summary: "Milk",
status: TodoItemStatus.NeedsAction,
},
{
uid: "13",
summary: "Eggs",
status: TodoItemStatus.NeedsAction,
},
{
uid: "14",
summary: "Oranges",
status: TodoItemStatus.Completed,
},
{
uid: "15",
summary: "Beer",
},
] as TodoItem[],
};
export const mockTodo = (hass: MockHomeAssistant) => {
hass.mockWS("todo/item/list", () => items);
hass.mockWS("todo/item/move", () => undefined);
hass.mockWS("todo/item/subscribe", (_msg, _hass, onChange) => {
onChange!(items);
// eslint-disable-next-line @typescript-eslint/no-empty-function
return () => {};
});
hass.mockWS("todo/item/list", () => ({
items: [
{
uid: "12",
summary: "Milk",
status: TodoItemStatus.NeedsAction,
},
{
uid: "13",
summary: "Eggs",
status: TodoItemStatus.NeedsAction,
},
{
uid: "14",
summary: "Oranges",
status: TodoItemStatus.Completed,
},
] 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,17 @@ 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",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/strict",
"plugin:@typescript-eslint/stylistic",
"plugin:wc/recommended",
"plugin:lit/all",
"plugin:lit-a11y/recommended",
"prettier"
),
{
plugins: {
"unused-imports": unusedImports,
@@ -42,9 +40,10 @@ export default tseslint.config(
__VERSION__: false,
__STATIC_PATH__: false,
__SUPERVISOR__: false,
Polymer: true,
},
parser: tseslint.parser,
parser: tsParser,
ecmaVersion: 2020,
sourceType: "module",
@@ -185,5 +184,5 @@ export default tseslint.config(
],
"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

@@ -1,5 +1,5 @@
import "./ha-gallery";
import("../../src/resources/append-ha-style");
import("../../src/resources/ha-style");
document.body.appendChild(document.createElement("ha-gallery"));

View File

@@ -1,30 +1,29 @@
import type { TemplateResult } from "lit";
import { LitElement, css, html } from "lit";
import { LitElement, html, css } from "lit";
import { customElement, state } from "lit/decorators";
import "../../../../src/components/ha-formfield";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import type { HomeAssistant } from "../../../../src/types";
import "../../components/demo-black-white-row";
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
import type { Action } from "../../../../src/data/script";
import "../../../../src/panels/config/automation/action/ha-automation-action";
import { HaChooseAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-choose";
import { HaConditionAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-condition";
import { HaDelayAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-delay";
import { HaDeviceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-device_id";
import { HaEventAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-event";
import { HaIfAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-if";
import { HaParallelAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-parallel";
import { HaPlayMediaAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-play_media";
import { HaRepeatAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-repeat";
import { HaSequenceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-sequence";
import { HaServiceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-service";
import { HaStopAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-stop";
import { HaWaitForTriggerAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger";
import { HaWaitAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-wait_template";
import type { Action } from "../../../../src/data/script";
import { HaConditionAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-condition";
import { HaSequenceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-sequence";
import { HaParallelAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-parallel";
import { HaIfAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-if";
import { HaStopAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-stop";
import { HaPlayMediaAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-play_media";
const SCHEMAS: { name: string; actions: Action[] }[] = [
{ name: "Event", actions: [HaEventAction.defaultConfig] },

View File

@@ -1,27 +1,26 @@
import type { TemplateResult } from "lit";
import { LitElement, css, html } from "lit";
import { LitElement, html, css } from "lit";
import { customElement, state } from "lit/decorators";
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
import "../../../../src/components/ha-formfield";
import type { ConditionWithShorthand } from "../../../../src/data/automation";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import type { HomeAssistant } from "../../../../src/types";
import "../../components/demo-black-white-row";
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
import type { ConditionWithShorthand } from "../../../../src/data/automation";
import "../../../../src/panels/config/automation/condition/ha-automation-condition";
import { HaAndCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-and";
import { HaDeviceCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-device";
import { HaNotCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-not";
import HaNumericStateCondition from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-numeric_state";
import { HaOrCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-or";
import { HaStateCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-state";
import { HaSunCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-sun";
import { HaTemplateCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-template";
import { HaTimeCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-time";
import { HaTriggerCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-trigger";
import { HaZoneCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-zone";
import type { HomeAssistant } from "../../../../src/types";
import "../../components/demo-black-white-row";
import { HaAndCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-and";
import { HaOrCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-or";
import { HaNotCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-not";
const SCHEMAS: { name: string; conditions: ConditionWithShorthand[] }[] = [
{

View File

@@ -1,36 +1,35 @@
import type { TemplateResult } from "lit";
import { LitElement, css, html } from "lit";
import { LitElement, html, css } from "lit";
import { customElement, state } from "lit/decorators";
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
import { mockAuth } from "../../../../demo/src/stubs/auth";
import { mockConfig } from "../../../../demo/src/stubs/config";
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
import { mockTags } from "../../../../demo/src/stubs/tags";
import "../../../../src/components/ha-formfield";
import type { Trigger } from "../../../../src/data/automation";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../../../src/panels/config/automation/trigger/ha-automation-trigger";
import { HaConversationTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-conversation";
import { HaDeviceTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-device";
import { HaEventTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-event";
import type { HomeAssistant } from "../../../../src/types";
import "../../components/demo-black-white-row";
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
import { mockConfig } from "../../../../demo/src/stubs/config";
import { mockTags } from "../../../../demo/src/stubs/tags";
import { mockAuth } from "../../../../demo/src/stubs/auth";
import type { Trigger } from "../../../../src/data/automation";
import { HaGeolocationTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-geo_location";
import { HaEventTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-event";
import { HaHassTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-homeassistant";
import { HaTriggerList } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-list";
import { HaMQTTTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt";
import { HaNumericStateTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-numeric_state";
import { HaPersistentNotificationTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-persistent_notification";
import { HaStateTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-state";
import { HaSunTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-sun";
import { HaTagTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-tag";
import { HaTemplateTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-template";
import { HaTimeTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-time";
import { HaTimePatternTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-time_pattern";
import { HaWebhookTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-webhook";
import { HaPersistentNotificationTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-persistent_notification";
import { HaZoneTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-zone";
import type { HomeAssistant } from "../../../../src/types";
import "../../components/demo-black-white-row";
import { HaDeviceTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-device";
import { HaStateTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-state";
import { HaMQTTTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt";
import "../../../../src/panels/config/automation/trigger/ha-automation-trigger";
import { HaConversationTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-conversation";
import { HaTriggerList } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-list";
const SCHEMAS: { name: string; triggers: Trigger[] }[] = [
{

View File

@@ -1,65 +0,0 @@
---
title: Badge
subtitle: Lovelace dashboard badge
---
<style>
.wrapper {
display: flex;
gap: 24px;
}
</style>
# Badge `<ha-badge>`
The badge component is a small component that displays a number or status information. It is used in the lovelace dashboard on the top.
## Implementation
### Example Usage
<div class="wrapper">
<ha-badge>
simple badge
</ha-badge>
<ha-badge label="Info">
With a label
</ha-badge>
<ha-badge type="button">
Type button
</ha-badge>
</div>
```html
<ha-badge> simple badge </ha-badge>
<ha-badge label="Info"> With a label </ha-badge>
<ha-badge type="button"> Type button </ha-badge>
```
### API
**Slots**
- default slot is the content of the badge
- no default
- `icon` set the icon of the badge
- no default
**Properties/Attributes**
| Name | Type | Default | Description |
| -------- | ----------------------- | ----------- | ------------------------------------------------------------ |
| type | `"badge"` or `"button"` | `"badge"` | If it's button it shows a ripple effect |
| label | string | `undefined` | Text label for the badge, only visible if `iconOnly = false` |
| iconOnly | boolean | `false` | Only show label |
**CSS Custom Properties**
- `--ha-badge-size` (default `36px`)
- `--ha-badge-border-radius` (default `calc(var(--ha-badge-size, 36px) / 2)`)
- `--ha-badge-font-size` (default `var(--ha-font-size-s)`)
- `--ha-badge-icon-size` (default `18px`)

View File

@@ -1,129 +0,0 @@
import { mdiButtonCursor, mdiHome } from "@mdi/js";
import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement } from "lit/decorators";
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
import "../../../../src/components/ha-badge";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-svg-icon";
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
const badges: {
type?: "badge" | "button";
label?: string;
iconOnly?: boolean;
slot?: TemplateResult;
iconSlot?: TemplateResult;
}[] = [
{
slot: html`<span>Badge</span>`,
},
{
type: "badge",
label: "Badge",
iconSlot: html`<ha-svg-icon slot="icon" .path=${mdiHome}></ha-svg-icon>`,
slot: html`<span>Badge</span>`,
},
{
type: "button",
label: "Button",
iconSlot: html`<ha-svg-icon
slot="icon"
.path=${mdiButtonCursor}
></ha-svg-icon>`,
slot: html`<span>Button</span>`,
},
{
type: "button",
label: "Label only",
iconSlot: html`<ha-svg-icon
slot="icon"
.path=${mdiButtonCursor}
></ha-svg-icon>`,
},
{
type: "button",
label: "Label",
slot: html`<span>Button no label</span>`,
},
{
label: "Icon only",
iconOnly: true,
iconSlot: html`<ha-svg-icon
slot="icon"
.path=${mdiHomeAssistant}
></ha-svg-icon>`,
},
];
@customElement("demo-components-ha-badge")
export class DemoHaBadge extends LitElement {
protected render(): TemplateResult {
return html`
${["light", "dark"].map(
(mode) => html`
<div class=${mode}>
<ha-card header="ha-badge ${mode} demo">
<div class="card-content">
${badges.map(
(badge) => html`
<ha-badge
.type=${badge.type || undefined}
.label=${badge.label}
.iconOnly=${badge.iconOnly || false}
>
${badge.iconSlot} ${badge.slot}
</ha-badge>
`
)}
</div>
</ha-card>
</div>
`
)}
`;
}
firstUpdated(changedProps) {
super.firstUpdated(changedProps);
applyThemesOnElement(
this.shadowRoot!.querySelector(".dark"),
{
default_theme: "default",
default_dark_theme: "default",
themes: {},
darkMode: true,
theme: "default",
},
undefined,
undefined,
true
);
}
static styles = css`
:host {
display: flex;
justify-content: center;
}
.dark,
.light {
display: block;
background-color: var(--primary-background-color);
padding: 0 50px;
}
ha-card {
margin: 24px auto;
}
.card-content {
display: flex;
gap: 24px;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"demo-components-ha-badge": DemoHaBadge;
}
}

View File

@@ -1,4 +1,4 @@
---
title: Spinner
title: Circular Progress
subtitle: Can be used to indicate an ongoing task.
---

View File

@@ -0,0 +1,63 @@
import type { TemplateResult } from "lit";
import { html, css, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../src/components/ha-bar";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-circular-progress";
import "@material/web/progress/circular-progress";
import type { HomeAssistant } from "../../../../src/types";
@customElement("demo-components-ha-circular-progress")
export class DemoHaCircularProgress extends LitElement {
@property({ attribute: false }) hass!: HomeAssistant;
protected render(): TemplateResult {
return html`<ha-card header="Basic circular progress">
<div class="card-content">
<ha-circular-progress indeterminate></ha-circular-progress></div
></ha-card>
<ha-card header="Different circular progress sizes">
<div class="card-content">
<ha-circular-progress
indeterminate
size="tiny"
></ha-circular-progress>
<ha-circular-progress
indeterminate
size="small"
></ha-circular-progress>
<ha-circular-progress
indeterminate
size="medium"
></ha-circular-progress>
<ha-circular-progress
indeterminate
size="large"
></ha-circular-progress></div
></ha-card>
<ha-card header="Circular progress with an aria-label">
<div class="card-content">
<ha-circular-progress
indeterminate
aria-label="Doing something..."
></ha-circular-progress>
<ha-circular-progress
indeterminate
.ariaLabel=${"Doing something..."}
></ha-circular-progress></div
></ha-card>`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"demo-components-ha-circular-progress": DemoHaCircularProgress;
}
}

View File

@@ -1,4 +1,4 @@
import { mdiLightbulbOn, mdiPacMan } from "@mdi/js";
import { mdiPacMan } from "@mdi/js";
import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement } from "lit/decorators";
@@ -125,23 +125,6 @@ const SAMPLES: {
`;
},
},
{
template(slot, leftChevron) {
return html`
<ha-expansion-panel
slot=${slot}
.leftChevron=${leftChevron}
header="Attr Header with actions"
>
<ha-svg-icon
slot="leading-icon"
.path=${mdiLightbulbOn}
></ha-svg-icon>
${SHORT_TEXT}
</ha-expansion-panel>
`;
},
},
];
@customElement("demo-components-ha-expansion-panel")

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,

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

@@ -6,23 +6,22 @@ import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
import { mockConfigEntries } from "../../../../demo/src/stubs/config_entries";
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
import { mockFloorRegistry } from "../../../../demo/src/stubs/floor_registry";
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
import { mockLabelRegistry } from "../../../../demo/src/stubs/label_registry";
import "../../../../src/components/ha-formfield";
import "../../../../src/components/ha-selector/ha-selector";
import "../../../../src/components/ha-settings-row";
import type { AreaRegistryEntry } from "../../../../src/data/area_registry";
import type { BlueprintInput } from "../../../../src/data/blueprint";
import type { DeviceRegistryEntry } from "../../../../src/data/device_registry";
import type { FloorRegistryEntry } from "../../../../src/data/floor_registry";
import type { LabelRegistryEntry } from "../../../../src/data/label_registry";
import { showDialog } from "../../../../src/dialogs/make-dialog-manager";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import type { ProvideHassElement } from "../../../../src/mixins/provide-hass-lit-mixin";
import type { HomeAssistant } from "../../../../src/types";
import "../../components/demo-black-white-row";
import type { FloorRegistryEntry } from "../../../../src/data/floor_registry";
import type { LabelRegistryEntry } from "../../../../src/data/label_registry";
import { mockFloorRegistry } from "../../../../demo/src/stubs/floor_registry";
import { mockLabelRegistry } from "../../../../demo/src/stubs/label_registry";
import type { DeviceRegistryEntry } from "../../../../src/data/device_registry";
const ENTITIES = [
getEntity("alarm_control_panel", "alarm", "disarmed", {
@@ -48,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,
@@ -72,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,
@@ -96,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,
@@ -644,6 +640,9 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
}
static styles = css`
ha-settings-row {
--paper-item-body-two-line-min-height: 0;
}
.options {
max-width: 800px;
margin: 16px auto;

View File

@@ -1,44 +0,0 @@
import type { TemplateResult } from "lit";
import { html, css, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../src/components/ha-bar";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-spinner";
import type { HomeAssistant } from "../../../../src/types";
@customElement("demo-components-ha-spinner")
export class DemoHaSpinner extends LitElement {
@property({ attribute: false }) hass!: HomeAssistant;
protected render(): TemplateResult {
return html`<ha-card header="Basic spinner">
<div class="card-content">
<ha-spinner></ha-spinner></div
></ha-card>
<ha-card header="Different spinner sizes">
<div class="card-content">
<ha-spinner size="tiny"></ha-spinner>
<ha-spinner size="small"></ha-spinner>
<ha-spinner size="medium"></ha-spinner>
<ha-spinner size="large"></ha-spinner></div
></ha-card>
<ha-card header="Spinner with an aria-label">
<div class="card-content">
<ha-spinner aria-label="Doing something..."></ha-spinner>
<ha-spinner .ariaLabel=${"Doing something..."}></ha-spinner></div
></ha-card>`;
}
static styles = css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"demo-components-ha-spinner": DemoHaSpinner;
}
}

View File

@@ -1,34 +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 shoelace `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)
- `--sl-tooltip-font-family` (Default: `var(--ha-font-family-body)`)
- `--ha-tooltip-font-size` (Default: `var(--ha-font-size-s)`)
- `--sl-tooltip-font-weight` (Default: `var(--ha-font-weight-normal)`)
- `--sl-tooltip-line-height` (Default: `var(--ha-line-height-condensed)`)

View File

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

View File

@@ -1,9 +1,9 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators";
import { formatDateTimeNumeric } from "../../../../src/common/datetime/format_date_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import "../../../../src/components/ha-list";
import type { FrontendLocaleData } from "../../../../src/data/translation";
import {
DateFormat,
@@ -49,7 +49,7 @@ export class DemoDateTimeDateTimeNumeric extends LitElement {
@value-changed=${this.handleValueChanged}
>
</ha-control-select>
<ha-list>
<mwc-list>
<div class="container header">
<div>Language</div>
<div class="center">Default (lang)</div>
@@ -96,7 +96,7 @@ export class DemoDateTimeDateTimeNumeric extends LitElement {
</div>
`
)}
</ha-list>
</mwc-list>
`;
}

View File

@@ -1,9 +1,9 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators";
import { formatDateTimeWithSeconds } from "../../../../src/common/datetime/format_date_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import "../../../../src/components/ha-list";
import type { FrontendLocaleData } from "../../../../src/data/translation";
import {
DateFormat,
@@ -49,7 +49,7 @@ export class DemoDateTimeDateTimeSeconds extends LitElement {
@value-changed=${this.handleValueChanged}
>
</ha-control-select>
<ha-list>
<mwc-list>
<div class="container header">
<div>Language</div>
<div class="center">Default (lang)</div>
@@ -96,7 +96,7 @@ export class DemoDateTimeDateTimeSeconds extends LitElement {
</div>
`
)}
</ha-list>
</mwc-list>
`;
}

View File

@@ -1,9 +1,9 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators";
import { formatShortDateTimeWithYear } from "../../../../src/common/datetime/format_date_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import "../../../../src/components/ha-list";
import type { FrontendLocaleData } from "../../../../src/data/translation";
import {
DateFormat,
@@ -49,7 +49,7 @@ export class DemoDateTimeDateTimeShortYear extends LitElement {
@value-changed=${this.handleValueChanged}
>
</ha-control-select>
<ha-list>
<mwc-list>
<div class="container header">
<div>Language</div>
<div class="center">Default (lang)</div>
@@ -96,7 +96,7 @@ export class DemoDateTimeDateTimeShortYear extends LitElement {
</div>
`
)}
</ha-list>
</mwc-list>
`;
}

View File

@@ -1,9 +1,9 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators";
import { formatShortDateTime } from "../../../../src/common/datetime/format_date_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import "../../../../src/components/ha-list";
import type { FrontendLocaleData } from "../../../../src/data/translation";
import {
DateFormat,
@@ -49,7 +49,7 @@ export class DemoDateTimeDateTimeShort extends LitElement {
@value-changed=${this.handleValueChanged}
>
</ha-control-select>
<ha-list>
<mwc-list>
<div class="container header">
<div>Language</div>
<div class="center">Default (lang)</div>
@@ -96,7 +96,7 @@ export class DemoDateTimeDateTimeShort extends LitElement {
</div>
`
)}
</ha-list>
</mwc-list>
`;
}

View File

@@ -1,9 +1,9 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators";
import { formatDateTime } from "../../../../src/common/datetime/format_date_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import "../../../../src/components/ha-list";
import type { FrontendLocaleData } from "../../../../src/data/translation";
import {
DateFormat,
@@ -49,7 +49,7 @@ export class DemoDateTimeDateTime extends LitElement {
@value-changed=${this.handleValueChanged}
>
</ha-control-select>
<ha-list>
<mwc-list>
<div class="container header">
<div>Language</div>
<div class="center">Default (lang)</div>
@@ -96,7 +96,7 @@ export class DemoDateTimeDateTime extends LitElement {
</div>
`
)}
</ha-list>
</mwc-list>
`;
}

View File

@@ -1,8 +1,8 @@
import "@material/mwc-list/mwc-list";
import { css, html, LitElement } from "lit";
import { customElement } from "lit/decorators";
import { formatDateNumeric } from "../../../../src/common/datetime/format_date";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-list";
import type { FrontendLocaleData } from "../../../../src/data/translation";
import {
DateFormat,
@@ -27,7 +27,7 @@ export class DemoDateTimeDate extends LitElement {
};
const date = new Date();
return html`
<ha-list>
<mwc-list>
<div class="container header">
<div>Language</div>
<div class="center">Default (lang)</div>
@@ -86,7 +86,7 @@ export class DemoDateTimeDate extends LitElement {
</div>
`
)}
</ha-list>
</mwc-list>
`;
}

View File

@@ -1,9 +1,9 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators";
import { formatTimeWithSeconds } from "../../../../src/common/datetime/format_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import "../../../../src/components/ha-list";
import type { FrontendLocaleData } from "../../../../src/data/translation";
import {
DateFormat,
@@ -49,7 +49,7 @@ export class DemoDateTimeTimeSeconds extends LitElement {
@value-changed=${this.handleValueChanged}
>
</ha-control-select>
<ha-list>
<mwc-list>
<div class="container header">
<div>Language</div>
<div class="center">Default (lang)</div>
@@ -96,7 +96,7 @@ export class DemoDateTimeTimeSeconds extends LitElement {
</div>
`
)}
</ha-list>
</mwc-list>
`;
}

View File

@@ -1,9 +1,9 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators";
import { formatTimeWeekday } from "../../../../src/common/datetime/format_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import "../../../../src/components/ha-list";
import type { FrontendLocaleData } from "../../../../src/data/translation";
import {
DateFormat,
@@ -49,7 +49,7 @@ export class DemoDateTimeTimeWeekday extends LitElement {
@value-changed=${this.handleValueChanged}
>
</ha-control-select>
<ha-list>
<mwc-list>
<div class="container header">
<div>Language</div>
<div class="center">Default (lang)</div>
@@ -96,7 +96,7 @@ export class DemoDateTimeTimeWeekday extends LitElement {
</div>
`
)}
</ha-list>
</mwc-list>
`;
}

View File

@@ -1,9 +1,9 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators";
import { formatTime } from "../../../../src/common/datetime/format_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import "../../../../src/components/ha-list";
import type { FrontendLocaleData } from "../../../../src/data/translation";
import {
DateFormat,
@@ -49,7 +49,7 @@ export class DemoDateTimeTime extends LitElement {
@value-changed=${this.handleValueChanged}
>
</ha-control-select>
<ha-list>
<mwc-list>
<div class="container header">
<div>Language</div>
<div class="center">Default (lang)</div>
@@ -96,7 +96,7 @@ export class DemoDateTimeTime extends LitElement {
</div>
`
)}
</ha-list>
</mwc-list>
`;
}

View File

@@ -1,11 +1,11 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { mockIcons } from "../../../../demo/src/stubs/icons";
import { mockTodo } from "../../../../demo/src/stubs/todo";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { getEntity } from "../../../../src/fake_data/entity";
import { mockTodo } from "../../../../demo/src/stubs/todo";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("todo", "shopping_list", "2", {

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,
@@ -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",

View File

@@ -1,5 +1,5 @@
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 { css, html, LitElement, nothing } from "lit";
@@ -11,7 +11,6 @@ import { navigate } from "../../../src/common/navigate";
import { extractSearchParam } from "../../../src/common/url/search-params";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-icon-button";
import "../../../src/components/ha-list-item";
import "../../../src/components/search-input";
import type { HassioAddonRepository } from "../../../src/data/hassio/addon";
import { reloadHassioAddons } from "../../../src/data/hassio/addon";
@@ -90,17 +89,17 @@ export class HassioAddonStore extends LitElement {
.path=${mdiDotsVertical}
slot="trigger"
></ha-icon-button>
<ha-list-item>
<mwc-list-item>
${this.supervisor.localize("store.check_updates")}
</ha-list-item>
<ha-list-item>
</mwc-list-item>
<mwc-list-item>
${this.supervisor.localize("store.repositories")}
</ha-list-item>
</mwc-list-item>
${this.hass.userData?.showAdvanced &&
atLeastVersion(this.hass.config.version, 0, 117)
? html`<ha-list-item>
? html`<mwc-list-item>
${this.supervisor.localize("store.registries")}
</ha-list-item>`
</mwc-list-item>`
: ""}
</ha-button-menu>
${repos.length === 0

View File

@@ -1,11 +1,12 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { stopPropagation } from "../../../../src/common/dom/stop_propagation";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-list-item";
import "../../../../src/components/ha-select";
import type {
HassioAddonDetails,
@@ -28,8 +29,6 @@ class HassioAddonAudio extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails;
@property({ type: Boolean }) public disabled = false;
@state() private _error?: string;
@state() private _inputDevices?: HassioHardwareAudioDevice[];
@@ -49,7 +48,7 @@ class HassioAddonAudio extends LitElement {
<div class="card-content">
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: nothing}
: ""}
${this._inputDevices &&
html`<ha-select
.label=${this.supervisor.localize(
@@ -60,13 +59,12 @@ class HassioAddonAudio extends LitElement {
fixedMenuPosition
naturalMenuWidth
.value=${this._selectedInput!}
.disabled=${this.disabled}
>
${this._inputDevices.map(
(item) => html`
<ha-list-item .value=${item.device || ""}>
<mwc-list-item .value=${item.device || ""}>
${item.name}
</ha-list-item>
</mwc-list-item>
`
)}
</ha-select>`}
@@ -80,22 +78,18 @@ class HassioAddonAudio extends LitElement {
fixedMenuPosition
naturalMenuWidth
.value=${this._selectedOutput!}
.disabled=${this.disabled}
>
${this._outputDevices.map(
(item) => html`
<ha-list-item .value=${item.device || ""}
>${item.name}</ha-list-item
<mwc-list-item .value=${item.device || ""}
>${item.name}</mwc-list-item
>
`
)}
</ha-select>`}
</div>
<div class="card-actions">
<ha-progress-button
.disabled=${this.disabled}
@click=${this._saveSettings}
>
<ha-progress-button @click=${this._saveSettings}>
${this.supervisor.localize("common.save")}
</ha-progress-button>
</div>
@@ -177,10 +171,6 @@ class HassioAddonAudio extends LitElement {
}
private async _saveSettings(ev: CustomEvent): Promise<void> {
if (this.disabled) {
return;
}
const button = ev.currentTarget as any;
button.progress = true;

View File

@@ -1,13 +1,12 @@
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../src/components/ha-spinner";
import "../../../../src/components/ha-circular-progress";
import type { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import type { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../../src/resources/styles";
import type { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style";
import "../info/hassio-addon-system-managed";
import "./hassio-addon-audio";
import "./hassio-addon-config";
import "./hassio-addon-network";
@@ -20,14 +19,9 @@ class HassioAddonConfigDashboard extends LitElement {
@property({ attribute: false }) public addon?: HassioAddonDetails;
@property({ type: Boolean }) public narrow = false;
@property({ type: Boolean, attribute: "control-enabled" })
public controlEnabled = false;
protected render(): TemplateResult {
if (!this.addon) {
return html`<ha-spinner></ha-spinner>`;
return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
}
const hasConfiguration =
(this.addon.options && Object.keys(this.addon.options).length) ||
@@ -35,16 +29,6 @@ class HassioAddonConfigDashboard extends LitElement {
return html`
<div class="content">
${this.addon.system_managed &&
(hasConfiguration || this.addon.network || this.addon.audio)
? html`
<hassio-addon-system-managed
.supervisor=${this.supervisor}
.narrow=${this.narrow}
.hideButton=${this.controlEnabled}
></hassio-addon-system-managed>
`
: nothing}
${hasConfiguration || this.addon.network || this.addon.audio
? html`
${hasConfiguration
@@ -53,33 +37,27 @@ class HassioAddonConfigDashboard extends LitElement {
.hass=${this.hass}
.addon=${this.addon}
.supervisor=${this.supervisor}
.disabled=${this.addon.system_managed &&
!this.controlEnabled}
></hassio-addon-config>
`
: nothing}
: ""}
${this.addon.network
? html`
<hassio-addon-network
.hass=${this.hass}
.addon=${this.addon}
.supervisor=${this.supervisor}
.disabled=${this.addon.system_managed &&
!this.controlEnabled}
></hassio-addon-network>
`
: nothing}
: ""}
${this.addon.audio
? html`
<hassio-addon-audio
.hass=${this.hass}
.addon=${this.addon}
.supervisor=${this.supervisor}
.disabled=${this.addon.system_managed &&
!this.controlEnabled}
></hassio-addon-audio>
`
: nothing}
: ""}
`
: this.supervisor.localize("addon.configuration.no_configuration")}
</div>

View File

@@ -1,4 +1,6 @@
import "@material/mwc-button";
import type { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js";
import { DEFAULT_SCHEMA, Type } from "js-yaml";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
@@ -14,7 +16,6 @@ import "../../../../src/components/ha-form/ha-form";
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
import "../../../../src/components/ha-formfield";
import "../../../../src/components/ha-icon-button";
import "../../../../src/components/ha-list-item";
import "../../../../src/components/ha-switch";
import "../../../../src/components/ha-yaml-editor";
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
@@ -60,8 +61,6 @@ class HassioAddonConfig extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ type: Boolean }) public disabled = false;
@state() private _configHasChanged = false;
@state() private _valid = true;
@@ -114,9 +113,8 @@ class HassioAddonConfig extends LitElement {
required: entry.required,
selector: {
text: {
type: entry.format
? entry.format
: MASKED_FIELDS.includes(entry.name)
type:
entry.format || MASKED_FIELDS.includes(entry.name)
? "password"
: "text",
},
@@ -177,7 +175,7 @@ class HassioAddonConfig extends LitElement {
.path=${mdiDotsVertical}
slot="trigger"
></ha-icon-button>
<ha-list-item .disabled=${!this._canShowSchema || this.disabled}>
<mwc-list-item .disabled=${!this._canShowSchema}>
${this._yamlMode
? this.supervisor.localize(
"addon.configuration.options.edit_in_ui"
@@ -185,13 +183,10 @@ class HassioAddonConfig extends LitElement {
: this.supervisor.localize(
"addon.configuration.options.edit_in_yaml"
)}
</ha-list-item>
<ha-list-item
class=${!this.disabled ? "warning" : ""}
.disabled=${this.disabled}
>
</mwc-list-item>
<mwc-list-item class="warning">
${this.supervisor.localize("common.reset_defaults")}
</ha-list-item>
</mwc-list-item>
</ha-button-menu>
</div>
</div>
@@ -199,7 +194,6 @@ class HassioAddonConfig extends LitElement {
<div class="card-content">
${showForm
? html`<ha-form
.disabled=${this.disabled}
.data=${this._options!}
@value-changed=${this._configChanged}
.computeLabel=${this.computeLabel}
@@ -213,7 +207,7 @@ class HassioAddonConfig extends LitElement {
)
)}
></ha-form>`
: html`<ha-yaml-editor
: html` <ha-yaml-editor
@value-changed=${this._configChanged}
.yamlSchema=${ADDON_YAML_SCHEMA}
></ha-yaml-editor>`}
@@ -249,9 +243,7 @@ class HassioAddonConfig extends LitElement {
<div class="card-actions right">
<ha-progress-button
@click=${this._saveTapped}
.disabled=${this.disabled ||
!this._configHasChanged ||
!this._valid}
.disabled=${!this._configHasChanged || !this._valid}
>
${this.supervisor.localize("common.save")}
</ha-progress-button>
@@ -353,10 +345,6 @@ class HassioAddonConfig extends LitElement {
}
private async _saveTapped(ev: CustomEvent): Promise<void> {
if (this.disabled || !this._configHasChanged || !this._valid) {
return;
}
const button = ev.currentTarget as any;
const options: Record<string, unknown> = this._yamlMode
? this._editor?.value
@@ -418,7 +406,7 @@ class HassioAddonConfig extends LitElement {
z-index: 3;
--mdc-theme-text-primary-on-background: var(--primary-text-color);
}
ha-list-item[disabled] {
mwc-list-item[disabled] {
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
}
.header {

View File

@@ -6,7 +6,6 @@ import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-formfield";
import "../../../../src/components/ha-form/ha-form";
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
import type {
@@ -29,8 +28,6 @@ class HassioAddonNetwork extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails;
@property({ type: Boolean }) public disabled = false;
@state() private _showOptional = false;
@state() private _configHasChanged = false;
@@ -68,10 +65,9 @@ class HassioAddonNetwork extends LitElement {
</p>
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: nothing}
: ""}
<ha-form
.disabled=${this.disabled}
.data=${this._config}
@value-changed=${this._configChanged}
.computeLabel=${this._computeLabel}
@@ -96,18 +92,14 @@ class HassioAddonNetwork extends LitElement {
>
</ha-switch>
</ha-formfield>`
: nothing}
: ""}
<div class="card-actions">
<ha-progress-button
class="warning"
.disabled=${this.disabled}
@click=${this._resetTapped}
>
<ha-progress-button class="warning" @click=${this._resetTapped}>
${this.supervisor.localize("common.reset_defaults")}
</ha-progress-button>
<ha-progress-button
@click=${this._saveTapped}
.disabled=${!this._configHasChanged || this.disabled}
.disabled=${!this._configHasChanged}
>
${this.supervisor.localize("common.save")}
</ha-progress-button>
@@ -163,10 +155,6 @@ class HassioAddonNetwork extends LitElement {
}
private async _resetTapped(ev: CustomEvent): Promise<void> {
if (this.disabled) {
return;
}
const button = ev.currentTarget as any;
const data: HassioAddonSetOptionParams = {
network: null,
@@ -198,10 +186,6 @@ class HassioAddonNetwork extends LitElement {
}
private async _saveTapped(ev: CustomEvent): Promise<void> {
if (!this._configHasChanged || this.disabled) {
return;
}
const button = ev.currentTarget as any;
this._error = undefined;

View File

@@ -2,7 +2,7 @@ import "../../../../src/components/ha-card";
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-spinner";
import "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-markdown";
import { customElement, property, state } from "lit/decorators";
import type { HassioAddonDetails } from "../../../../src/data/hassio/addon";
@@ -33,7 +33,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
protected render(): TemplateResult {
if (!this.addon) {
return html`<ha-spinner></ha-spinner>`;
return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
}
return html`
<div class="content">

View File

@@ -11,6 +11,7 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../../../src/common/dom/fire_event";
import { navigate } from "../../../src/common/navigate";
import { extractSearchParam } from "../../../src/common/url/search-params";
import "../../../src/components/ha-circular-progress";
import type { HassioAddonDetails } from "../../../src/data/hassio/addon";
import {
fetchAddonInfo,
@@ -52,9 +53,6 @@ class HassioAddonDashboard extends LitElement {
@property({ type: Boolean }) public narrow = false;
@state()
private _controlEnabled = false;
@state() private _error?: string;
private _backPath = new URLSearchParams(window.parent.location.search).get(
@@ -137,17 +135,11 @@ class HassioAddonDashboard extends LitElement {
.hass=${this.hass}
.supervisor=${this.supervisor}
.addon=${this.addon}
.controlEnabled=${this._controlEnabled}
@system-managed-take-control=${this._enableControl}
></hassio-addon-router>
</hass-tabs-subpage>
`;
}
private _enableControl() {
this._controlEnabled = true;
}
static get styles(): CSSResultGroup {
return [
haStyle,

View File

@@ -23,9 +23,6 @@ class HassioAddonRouter extends HassRouterPage {
| HassioAddonDetails
| StoreAddonDetails;
@property({ type: Boolean, attribute: "control-enabled" })
public controlEnabled = false;
protected routerOptions: RouterOptions = {
defaultPage: "info",
showLoading: true,
@@ -51,7 +48,6 @@ class HassioAddonRouter extends HassRouterPage {
el.supervisor = this.supervisor;
el.addon = this.addon;
el.narrow = this.narrow;
el.controlEnabled = this.controlEnabled;
}
}

View File

@@ -1,7 +1,7 @@
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../src/components/ha-spinner";
import "../../../../src/components/ha-circular-progress";
import type { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import type { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../../src/resources/styles";
@@ -21,12 +21,9 @@ class HassioAddonInfoDashboard extends LitElement {
@property({ attribute: false }) public addon?: HassioAddonDetails;
@property({ type: Boolean, attribute: "control-enabled" })
public controlEnabled = false;
protected render(): TemplateResult {
if (!this.addon) {
return html`<ha-spinner></ha-spinner>`;
return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
}
return html`
@@ -37,7 +34,6 @@ class HassioAddonInfoDashboard extends LitElement {
.hass=${this.hass}
.supervisor=${this.supervisor}
.addon=${this.addon}
.controlEnabled=${this.controlEnabled}
></hassio-addon-info>
</div>
`;

View File

@@ -1,6 +1,8 @@
import "@material/mwc-button";
import {
mdiCheckCircle,
mdiChip,
mdiPlayCircle,
mdiCircleOffOutline,
mdiCursorDefaultClickOutline,
mdiDocker,
@@ -17,30 +19,27 @@ import {
mdiNumeric6,
mdiNumeric7,
mdiNumeric8,
mdiPlayCircle,
mdiPound,
mdiShield,
} from "@mdi/js";
import type { CSSResultGroup, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { LitElement, css, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../../src/common/config/version";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { navigate } from "../../../../src/common/navigate";
import { capitalizeFirstLetter } from "../../../../src/common/string/capitalize-first-letter";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/chips/ha-assist-chip";
import "../../../../src/components/chips/ha-chip-set";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-formfield";
import "../../../../src/components/chips/ha-chip-set";
import "../../../../src/components/chips/ha-assist-chip";
import "../../../../src/components/ha-markdown";
import "../../../../src/components/ha-settings-row";
import "../../../../src/components/ha-svg-icon";
import "../../../../src/components/ha-switch";
import "../../../../src/components/ha-formfield";
import type { HaSwitch } from "../../../../src/components/ha-switch";
import type {
AddonCapability,
@@ -82,11 +81,10 @@ import { bytesToString } from "../../../../src/util/bytes-to-string";
import "../../components/hassio-card-content";
import "../../components/supervisor-metric";
import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown";
import { showSystemManagedDialog } from "../../dialogs/system-managed/show-dialog-system-managed";
import { hassioStyle } from "../../resources/hassio-style";
import "../../update-available/update-available-card";
import { addonArchIsSupported, extractChangelog } from "../../util/addon";
import "./hassio-addon-system-managed";
import { capitalizeFirstLetter } from "../../../../src/common/string/capitalize-first-letter";
const STAGE_ICON = {
stable: mdiCheckCircle,
@@ -119,9 +117,6 @@ class HassioAddonInfo extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ type: Boolean, attribute: "control-enabled" })
public controlEnabled = false;
@state() private _metrics?: HassioStats;
@state() private _error?: string;
@@ -160,9 +155,6 @@ class HassioAddonInfo extends LitElement {
)}`,
},
];
const systemManaged = this._isSystemManaged(this.addon);
return html`
${this.addon.update_available
? html`
@@ -174,7 +166,7 @@ class HassioAddonInfo extends LitElement {
@update-complete=${this._updateComplete}
></update-available-card>
`
: nothing}
: ""}
${"protected" in this.addon && !this.addon.protected
? html`
<ha-alert
@@ -186,31 +178,22 @@ class HassioAddonInfo extends LitElement {
${this.supervisor.localize(
"addon.dashboard.protection_mode.content"
)}
<ha-button
<mwc-button
slot="action"
.label=${this.supervisor.localize(
"addon.dashboard.protection_mode.enable"
)}
@click=${this._protectionToggled}
>
</ha-button>
</mwc-button>
</ha-alert>
`
: nothing}
${systemManaged
? html`
<hassio-addon-system-managed
.supervisor=${this.supervisor}
.narrow=${this.narrow}
.hideButton=${this.controlEnabled}
></hassio-addon-system-managed>
`
: nothing}
: ""}
<ha-card outlined>
<div class="card-content">
<div class="addon-header">
${!this.narrow ? this.addon.name : nothing}
${!this.narrow ? this.addon.name : ""}
<div class="addon-version light-color">
${this.addon.version
? html`
@@ -283,7 +266,7 @@ class HassioAddonInfo extends LitElement {
</ha-svg-icon>
</ha-assist-chip>
`
: nothing}
: ""}
<ha-assist-chip
filled
@@ -318,7 +301,7 @@ class HassioAddonInfo extends LitElement {
<ha-svg-icon slot="icon" .path=${mdiNetwork}> </ha-svg-icon>
</ha-assist-chip>
`
: nothing}
: ""}
${this.addon.full_access
? html`
<ha-assist-chip
@@ -334,7 +317,7 @@ class HassioAddonInfo extends LitElement {
<ha-svg-icon slot="icon" .path=${mdiChip}></ha-svg-icon>
</ha-assist-chip>
`
: nothing}
: ""}
${this.addon.homeassistant_api
? html`
<ha-assist-chip
@@ -353,7 +336,7 @@ class HassioAddonInfo extends LitElement {
></ha-svg-icon>
</ha-assist-chip>
`
: nothing}
: ""}
${this._computeHassioApi
? html`
<ha-assist-chip
@@ -372,7 +355,7 @@ class HassioAddonInfo extends LitElement {
></ha-svg-icon>
</ha-assist-chip>
`
: nothing}
: ""}
${this.addon.docker_api
? html`
<ha-assist-chip
@@ -388,7 +371,7 @@ class HassioAddonInfo extends LitElement {
<ha-svg-icon slot="icon" .path=${mdiDocker}></ha-svg-icon>
</ha-assist-chip>
`
: nothing}
: ""}
${this.addon.host_pid
? html`
<ha-assist-chip
@@ -404,7 +387,7 @@ class HassioAddonInfo extends LitElement {
<ha-svg-icon slot="icon" .path=${mdiPound}></ha-svg-icon>
</ha-assist-chip>
`
: nothing}
: ""}
${this.addon.apparmor !== "default"
? html`
<ha-assist-chip
@@ -421,7 +404,7 @@ class HassioAddonInfo extends LitElement {
<ha-svg-icon slot="icon" .path=${mdiShield}></ha-svg-icon>
</ha-assist-chip>
`
: nothing}
: ""}
${this.addon.auth_api
? html`
<ha-assist-chip
@@ -437,7 +420,7 @@ class HassioAddonInfo extends LitElement {
<ha-svg-icon slot="icon" .path=${mdiKey}></ha-svg-icon>
</ha-assist-chip>
`
: nothing}
: ""}
${this.addon.ingress
? html`
<ha-assist-chip
@@ -456,7 +439,7 @@ class HassioAddonInfo extends LitElement {
></ha-svg-icon>
</ha-assist-chip>
`
: nothing}
: ""}
${this.addon.signed
? html`
<ha-assist-chip
@@ -472,24 +455,7 @@ class HassioAddonInfo extends LitElement {
<ha-svg-icon slot="icon" .path=${mdiLinkLock}></ha-svg-icon>
</ha-assist-chip>
`
: nothing}
${systemManaged
? html`
<ha-assist-chip
filled
@click=${this._showSystemManagedDialog}
id="system_managed"
.label=${capitalizeFirstLetter(
this.supervisor.localize("addon.system_managed.badge")
)}
>
<ha-svg-icon
slot="icon"
.path=${mdiHomeAssistant}
></ha-svg-icon>
</ha-assist-chip>
`
: nothing}
: ""}
</ha-chip-set>
<div class="description light-color">
@@ -513,7 +479,7 @@ class HassioAddonInfo extends LitElement {
src="/api/hassio/addons/${this.addon.slug}/logo"
/>
`
: nothing}
: ""}
${this.addon.version
? html`
<div
@@ -534,7 +500,6 @@ class HassioAddonInfo extends LitElement {
)}
</span>
<ha-switch
.disabled=${systemManaged && !this.controlEnabled}
@change=${this._startOnBootToggled}
.checked=${this.addon.boot === "auto"}
haptic
@@ -555,15 +520,13 @@ class HassioAddonInfo extends LitElement {
)}
</span>
<ha-switch
.disabled=${systemManaged &&
!this.controlEnabled}
@change=${this._watchdogToggled}
.checked=${this.addon.watchdog || false}
.checked=${this.addon.watchdog}
haptic
></ha-switch>
</ha-settings-row>
`
: nothing}
: ""}
${this.addon.auto_update ||
this.hass.userData?.showAdvanced
? html`
@@ -579,15 +542,13 @@ class HassioAddonInfo extends LitElement {
)}
</span>
<ha-switch
.disabled=${systemManaged &&
!this.controlEnabled}
@change=${this._autoUpdateToggled}
.checked=${this.addon.auto_update}
haptic
></ha-switch>
</ha-settings-row>
`
: nothing}
: ""}
${!this._computeCannotIngressSidebar && this.addon.ingress
? html`
<ha-settings-row ?three-line=${this.narrow}>
@@ -602,15 +563,13 @@ class HassioAddonInfo extends LitElement {
)}
</span>
<ha-switch
.disabled=${systemManaged &&
!this.controlEnabled}
@change=${this._panelToggled}
.checked=${this.addon.ingress_panel}
haptic
></ha-switch>
</ha-settings-row>
`
: nothing}
: ""}
${this._computeUsesProtectedOptions
? html`
<ha-settings-row ?three-line=${this.narrow}>
@@ -625,18 +584,16 @@ class HassioAddonInfo extends LitElement {
)}
</span>
<ha-switch
.disabled=${systemManaged &&
!this.controlEnabled}
@change=${this._protectionToggled}
.checked=${this.addon.protected}
haptic
></ha-switch>
</ha-settings-row>
`
: nothing}
: ""}
</div>
`
: nothing}
: ""}
</div>
<div>
${this.addon.version && this.addon.state === "started"
@@ -655,12 +612,12 @@ class HassioAddonInfo extends LitElement {
></supervisor-metric>
`
)}`
: nothing}
: ""}
</div>
</div>
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: nothing}
: ""}
${!this.addon.version && addonStoreInfo && !this.addon.available
? !addonArchIsSupported(
this.supervisor.info.supported_arch,
@@ -684,7 +641,7 @@ class HassioAddonInfo extends LitElement {
)}
</ha-alert>
`
: nothing}
: ""}
</div>
<div class="card-actions">
<div>
@@ -694,7 +651,6 @@ class HassioAddonInfo extends LitElement {
<ha-progress-button
class="warning"
@click=${this._stopClicked}
.disabled=${systemManaged && !this.controlEnabled}
>
${this.supervisor.localize("addon.dashboard.stop")}
</ha-progress-button>
@@ -732,27 +688,26 @@ class HassioAddonInfo extends LitElement {
target="_blank"
rel="noopener"
>
<ha-button>
<mwc-button>
${this.supervisor.localize(
"addon.dashboard.open_web_ui"
)}
</ha-button>
</mwc-button>
</a>
`
: nothing}
: ""}
${this._computeShowIngressUI
? html`
<ha-button @click=${this._openIngress}>
<mwc-button @click=${this._openIngress}>
${this.supervisor.localize(
"addon.dashboard.open_web_ui"
)}
</ha-button>
</mwc-button>
`
: nothing}
: ""}
<ha-progress-button
class="warning"
@click=${this._uninstallClicked}
.disabled=${systemManaged && !this.controlEnabled}
>
${this.supervisor.localize("addon.dashboard.uninstall")}
</ha-progress-button>
@@ -765,8 +720,8 @@ class HassioAddonInfo extends LitElement {
${this.supervisor.localize("addon.dashboard.rebuild")}
</ha-progress-button>
`
: nothing}`
: nothing}
: ""}`
: ""}
</div>
</div>
</ha-card>
@@ -782,7 +737,7 @@ class HassioAddonInfo extends LitElement {
</div>
</ha-card>
`
: nothing}
: ""}
`;
}
@@ -867,13 +822,6 @@ class HassioAddonInfo extends LitElement {
});
}
private _showSystemManagedDialog() {
showSystemManagedDialog(this, {
addon: this.addon as HassioAddonDetails,
supervisor: this.supervisor,
});
}
private get _computeIsRunning(): boolean {
return (this.addon as HassioAddonDetails)?.state === "started";
}
@@ -1066,10 +1014,6 @@ class HassioAddonInfo extends LitElement {
}
private async _stopClicked(ev: CustomEvent): Promise<void> {
if (this._isSystemManaged(this.addon) && !this.controlEnabled) {
return;
}
const button = ev.currentTarget as any;
button.progress = true;
@@ -1181,10 +1125,6 @@ class HassioAddonInfo extends LitElement {
}
private async _uninstallClicked(ev: CustomEvent): Promise<void> {
if (this._isSystemManaged(this.addon) && !this.controlEnabled) {
return;
}
const button = ev.currentTarget as any;
button.progress = true;
let removeData = false;
@@ -1239,11 +1179,6 @@ class HassioAddonInfo extends LitElement {
button.progress = false;
}
private _isSystemManaged = memoizeOne(
(addon: HassioAddonDetails | StoreAddonDetails) =>
"system_managed" in addon && addon.system_managed
);
static get styles(): CSSResultGroup {
return [
haStyle,
@@ -1266,7 +1201,7 @@ class HassioAddonInfo extends LitElement {
ha-card.warning .card-content {
color: white;
}
ha-card.warning ha-button {
ha-card.warning mwc-button {
--mdc-theme-primary: white !important;
}
.warning {
@@ -1311,7 +1246,7 @@ class HassioAddonInfo extends LitElement {
ha-svg-icon.stopped {
color: var(--error-color);
}
protection-enable ha-button {
protection-enable mwc-button {
--mdc-theme-primary: white;
}
.description a {
@@ -1393,15 +1328,9 @@ class HassioAddonInfo extends LitElement {
align-self: end;
}
ha-alert ha-button {
ha-alert mwc-button {
--mdc-theme-primary: var(--primary-text-color);
}
:host > ha-alert {
display: block;
margin-bottom: 16px;
}
a {
text-decoration: none;
}

View File

@@ -1,60 +0,0 @@
import "@material/mwc-button";
import type { TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-button";
import type { Supervisor } from "../../../../src/data/supervisor/supervisor";
@customElement("hassio-addon-system-managed")
class HassioAddonSystemManaged extends LitElement {
@property({ type: Boolean }) public narrow = false;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ type: Boolean, attribute: "hide-button" }) public hideButton =
false;
protected render(): TemplateResult {
return html`
<ha-alert
alert-type="warning"
.title=${this.supervisor.localize("addon.system_managed.title")}
.narrow=${this.narrow}
>
${this.supervisor.localize("addon.system_managed.description")}
${!this.hideButton
? html`
<ha-button slot="action" @click=${this._takeControl}>
${this.supervisor.localize("addon.system_managed.take_control")}
</ha-button>
`
: nothing}
</ha-alert>
`;
}
private _takeControl() {
fireEvent(this, "system-managed-take-control");
}
static styles = css`
ha-alert {
display: block;
margin-bottom: 16px;
}
ha-button {
white-space: nowrap;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"hassio-addon-system-managed": HassioAddonSystemManaged;
}
interface HASSDomEvents {
"system-managed-take-control": undefined;
}
}

View File

@@ -6,7 +6,7 @@ import {
type TemplateResult,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../src/components/ha-spinner";
import "../../../../src/components/ha-circular-progress";
import type { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import type { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../../src/resources/styles";
@@ -28,7 +28,9 @@ class HassioAddonLogDashboard extends LitElement {
protected render(): TemplateResult {
if (!this.addon) {
return html` <ha-spinner></ha-spinner> `;
return html`
<ha-circular-progress indeterminate></ha-circular-progress>
`;
}
return html`
<div class="search">

View File

@@ -1,6 +1,6 @@
import "@material/mwc-button";
import type { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiBackupRestore, mdiDelete, mdiDotsVertical, mdiPlus } from "@mdi/js";
import type { CSSResultGroup, PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
@@ -18,7 +18,6 @@ import type {
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-fab";
import "../../../src/components/ha-icon-button";
import "../../../src/components/ha-list-item";
import "../../../src/components/ha-svg-icon";
import type { HassioBackup } from "../../../src/data/hassio/backup";
import {
@@ -33,7 +32,6 @@ import {
showAlertDialog,
showConfirmationDialog,
} from "../../../src/dialogs/generic/show-dialog-box";
import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-tabs-subpage-data-table";
import type { HaTabsSubpageDataTable } from "../../../src/layouts/hass-tabs-subpage-data-table";
import { haStyle } from "../../../src/resources/styles";
@@ -44,6 +42,7 @@ import { showHassioBackupDialog } from "../dialogs/backup/show-dialog-hassio-bac
import { showHassioCreateBackupDialog } from "../dialogs/backup/show-dialog-hassio-create-backup";
import { supervisorTabs } from "../hassio-tabs";
import { hassioStyle } from "../resources/hassio-style";
import "../../../src/layouts/hass-loading-screen";
type BackupItem = HassioBackup & {
secondary: string;
@@ -212,16 +211,16 @@ export class HassioBackups extends LitElement {
.path=${mdiDotsVertical}
slot="trigger"
></ha-icon-button>
<ha-list-item>
<mwc-list-item>
${this.supervisor.localize("common.reload")}
</ha-list-item>
<ha-list-item>
</mwc-list-item>
<mwc-list-item>
${this.supervisor.localize("dialog.backup_location.title")}
</ha-list-item>
</mwc-list-item>
${atLeastVersion(this.hass.config.version, 0, 116)
? html`<ha-list-item>
? html`<mwc-list-item>
${this.supervisor.localize("backup.upload_backup")}
</ha-list-item>`
</mwc-list-item>`
: ""}
</ha-button-menu>
@@ -254,9 +253,13 @@ export class HassioBackups extends LitElement {
"backup.delete_selected"
)}
.path=${mdiDelete}
id="delete-btn"
class="warning"
@click=${this._deleteSelected}
></ha-icon-button>
<simple-tooltip animation-delay="0" for="delete-btn">
${this.supervisor.localize("backup.delete_selected")}
</simple-tooltip>
`}
</div>
</div> `

View File

@@ -85,7 +85,7 @@ class HassioCardContent extends LitElement {
}
ha-svg-icon.hassupdate,
ha-svg-icon.backup {
color: var(--state-icon-color);
color: var(--paper-item-icon-color);
}
ha-svg-icon.not_available {
color: var(--error-color);

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

@@ -3,6 +3,7 @@ import type { TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/ha-circular-progress";
import "../../../src/components/ha-file-upload";
import type { HassioBackup } from "../../../src/data/hassio/backup";
import { uploadBackup } from "../../../src/data/hassio/backup";
@@ -13,7 +14,7 @@ import type { LocalizeFunc } from "../../../src/common/translations/localize";
declare global {
interface HASSDomEvents {
"hassio-backup-uploaded": { backup: HassioBackup };
"backup-uploaded": { backup: HassioBackup };
"backup-cleared": undefined;
}
}
@@ -69,7 +70,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

@@ -5,6 +5,7 @@ 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;
@@ -63,6 +67,8 @@ const _computeAddons = (addons): AddonCheckboxItem[] =>
export class SupervisorBackupContent extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public localize?: LocalizeFunc;
@property({ attribute: false }) public supervisor?: Supervisor;
@property({ attribute: false }) public backup?: HassioBackupDetail;
@@ -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"
@@ -196,7 +202,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 +222,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 +247,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 +259,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 +267,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}

View File

@@ -130,7 +130,7 @@ export class HassioUpdate extends LitElement {
color: var(--primary-text-color);
}
.update-heading {
font-size: var(--ha-font-size-l);
font-size: var(--paper-font-subhead_-_font-size);
font-weight: 500;
margin-bottom: 0.5em;
color: var(--primary-text-color);

View File

@@ -72,7 +72,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

@@ -1,5 +1,5 @@
import type { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiClose, mdiDotsVertical } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
@@ -8,17 +8,14 @@ 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/buttons/ha-progress-button";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-button-menu";
import "../../../../src/components/ha-dialog-header";
import "../../../../src/components/ha-header-bar";
import "../../../../src/components/ha-icon-button";
import "../../../../src/components/ha-list-item";
import "../../../../src/components/ha-md-dialog";
import type { HaMdDialog } from "../../../../src/components/ha-md-dialog";
import "../../../../src/components/ha-spinner";
import { getSignedPath } from "../../../../src/data/auth";
import type { HassioBackupDetail } from "../../../../src/data/hassio/backup";
import {
@@ -38,13 +35,15 @@ 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 { BackupOrRestoreKey } from "../../util/translations";
import type { HaMdDialog } from "../../../../src/components/ha-md-dialog";
@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;
@@ -63,13 +62,9 @@ class HassioBackupDialog
this._dialogParams = dialogParams;
this._backup = await fetchHassioBackupInfo(this.hass, dialogParams.slug);
if (!this._backup) {
this._error = this._dialogParams.supervisor?.localize(
"backup.no_backup_found"
);
this._error = this._localize("no_backup_found");
} else if (this._dialogParams.onboarding && !this._backup.homeassistant) {
this._error = this._dialogParams.supervisor?.localize(
"backup.restore_no_home_assistant"
);
this._error = this._localize("restore_no_home_assistant");
}
this._restoringBackup = false;
}
@@ -87,6 +82,13 @@ class HassioBackupDialog
return true;
}
private _localize(key: BackupOrRestoreKey) {
return (
this._dialogParams!.supervisor?.localize(`backup.${key}`) ||
this._dialogParams!.localize!(`ui.panel.page-onboarding.restore.${key}`)
);
}
protected render() {
if (!this._dialogParams || !this._backup) {
return nothing;
@@ -100,7 +102,7 @@ class HassioBackupDialog
<ha-dialog-header slot="headline">
<ha-icon-button
slot="navigationIcon"
.label=${this._dialogParams.supervisor?.localize("backup.close")}
.label=${this._localize("close")}
.path=${mdiClose}
@click=${this.closeDialog}
.disabled=${this._restoringBackup}
@@ -122,15 +124,15 @@ class HassioBackupDialog
.path=${mdiDotsVertical}
slot="trigger"
></ha-icon-button>
<ha-list-item
<mwc-list-item
>${this._dialogParams.supervisor.localize(
"backup.download_backup"
)}</ha-list-item
)}</mwc-list-item
>
<ha-list-item class="error"
<mwc-list-item class="error"
>${this._dialogParams.supervisor.localize(
"backup.delete_backup_title"
)}</ha-list-item
)}</mwc-list-item
>
</ha-button-menu>`
: nothing}
@@ -140,7 +142,7 @@ class HassioBackupDialog
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: this._restoringBackup
? html`<div class="loading">
<ha-spinner></ha-spinner>
<ha-circular-progress indeterminate></ha-circular-progress>
</div>`
: html`
<supervisor-backup-content
@@ -148,6 +150,7 @@ class HassioBackupDialog
.supervisor=${this._dialogParams.supervisor}
.backup=${this._backup}
.onboarding=${this._dialogParams.onboarding || false}
.localize=${this._dialogParams.localize}
dialogInitialFocus
>
</supervisor-backup-content>
@@ -158,7 +161,7 @@ class HassioBackupDialog
.disabled=${this._restoringBackup || !!this._error}
@click=${this._restoreClicked}
>
${this._dialogParams.supervisor?.localize("backup.restore")}
${this._localize("restore")}
</ha-button>
</div>
</ha-md-dialog>
@@ -193,22 +196,18 @@ class HassioBackupDialog
}
if (
!(await showConfirmationDialog(this, {
title: supervisor?.localize(
`backup.${
this._backup!.type === "full"
? "confirm_restore_full_backup_title"
: "confirm_restore_partial_backup_title"
}`
title: this._localize(
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"
}`
text: this._localize(
this._backup!.type === "full"
? "confirm_restore_full_backup_text"
: "confirm_restore_partial_backup_text"
),
confirmText: supervisor?.localize("backup.restore"),
dismissText: supervisor?.localize("backup.cancel"),
confirmText: this._localize("restore"),
dismissText: this._localize("cancel"),
}))
) {
this._restoringBackup = false;
@@ -228,8 +227,7 @@ class HassioBackupDialog
this.closeDialog();
} catch (error: any) {
this._error =
error?.body?.message ||
supervisor?.localize("backup.restore_start_failed");
error?.body?.message || this._localize("restore_start_failed");
} finally {
this._restoringBackup = false;
}
@@ -288,7 +286,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;
@@ -304,7 +302,7 @@ class HassioBackupDialog
private get _computeName() {
return this._backup
? this._backup.name || this._backup.slug
: this._dialogParams!.supervisor?.localize("backup.unnamed_backup") || "";
: this._localize("unnamed_backup");
}
static get styles(): CSSResultGroup {
@@ -312,6 +310,10 @@ class HassioBackupDialog
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);

View File

@@ -5,7 +5,6 @@ import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-spinner";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
import {
createHassioFullBackup,
@@ -59,7 +58,7 @@ class HassioCreateBackupDialog extends LitElement {
)}
>
${this._creatingBackup
? html`<ha-spinner></ha-spinner>`
? html`<ha-circular-progress indeterminate></ha-circular-progress>`
: html`<supervisor-backup-content
.hass=${this.hass}
.supervisor=${this._dialogParams.supervisor}
@@ -143,6 +142,10 @@ class HassioCreateBackupDialog extends LitElement {
:host {
direction: var(--direction);
}
ha-circular-progress {
display: block;
text-align: center;
}
`,
];
}

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

@@ -1,12 +1,12 @@
import "@material/mwc-list/mwc-list-item";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-list-item";
import "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-select";
import "../../../../src/components/ha-spinner";
import "../../../../src/components/ha-dialog";
import {
extractApiErrorMessage,
ignoreSupervisorError,
@@ -69,7 +69,12 @@ class HassioDatadiskDialog extends LitElement {
?hideActions=${this.moving}
>
${this.moving
? html`<ha-spinner aria-label="Moving" size="large"></ha-spinner>
? html` <ha-circular-progress
aria-label="Moving"
size="large"
indeterminate
>
</ha-circular-progress>
<p class="progress-text">
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.moving_desc"
@@ -95,8 +100,8 @@ class HassioDatadiskDialog extends LitElement {
>
${this.devices.map(
(device) =>
html`<ha-list-item .value=${device}
>${device}</ha-list-item
html`<mwc-list-item .value=${device}
>${device}</mwc-list-item
>`
)}
</ha-select>
@@ -161,7 +166,7 @@ class HassioDatadiskDialog extends LitElement {
ha-select {
width: 100%;
}
ha-spinner {
ha-circular-progress {
display: block;
margin: 32px;
text-align: center;

View File

@@ -16,14 +16,23 @@ import type { HomeAssistant } from "../../../../src/types";
import type { HassioHardwareDialogParams } from "./show-dialog-hassio-hardware";
const _filterDevices = memoizeOne(
(hardware: HassioHardwareInfo, filter: string, language: string) =>
(
showAdvanced: boolean,
hardware: HassioHardwareInfo,
filter: string,
language: string
) =>
hardware.devices
.filter(
(device) =>
device.by_id?.toLowerCase().includes(filter) ||
device.name.toLowerCase().includes(filter) ||
device.dev_path.toLocaleLowerCase().includes(filter) ||
JSON.stringify(device.attributes).toLocaleLowerCase().includes(filter)
(showAdvanced ||
["tty", "gpio", "input"].includes(device.subsystem)) &&
(device.by_id?.toLowerCase().includes(filter) ||
device.name.toLowerCase().includes(filter) ||
device.dev_path.toLocaleLowerCase().includes(filter) ||
JSON.stringify(device.attributes)
.toLocaleLowerCase()
.includes(filter))
)
.sort((a, b) => stringCompare(a.name, b.name, language))
);
@@ -51,6 +60,7 @@ class HassioHardwareDialog extends LitElement {
}
const devices = _filterDevices(
this.hass.userData?.showAdvanced || false,
this._dialogParams.hardware,
(this._filter || "").toLowerCase(),
this.hass.locale.language
@@ -170,7 +180,7 @@ class HassioHardwareDialog extends LitElement {
padding: 16px;
overflow: auto;
line-height: 1.45;
font-family: var(--ha-font-family-code);
font-family: var(--code-font-family, monospace);
}
code {
font-size: 85%;

View File

@@ -38,7 +38,6 @@ class HassioMarkdownDialog extends LitElement {
open
@closed=${this.closeDialog}
.heading=${createCloseHeading(this.hass, this.title)}
hideactions
>
<ha-markdown
.content=${this.content || ""}

View File

@@ -1,4 +1,8 @@
import "@material/mwc-button/mwc-button";
import "@material/mwc-list/mwc-list";
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-tab";
import "@material/mwc-tab-bar";
import { mdiClose } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
@@ -6,16 +10,14 @@ import { customElement, property, state } from "lit/decorators";
import { cache } from "lit/directives/cache";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-expansion-panel";
import "../../../../src/components/ha-formfield";
import "../../../../src/components/ha-header-bar";
import "../../../../src/components/ha-icon-button";
import "../../../../src/components/ha-list";
import "../../../../src/components/ha-list-item";
import "../../../../src/components/ha-password-field";
import "../../../../src/components/ha-radio";
import "../../../../src/components/ha-spinner";
import "../../../../src/components/ha-textfield";
import type { HaTextField } from "../../../../src/components/ha-textfield";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
@@ -37,7 +39,6 @@ import type { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
import { haStyleDialog } from "../../../../src/resources/styles";
import type { HomeAssistant } from "../../../../src/types";
import type { HassioNetworkDialogParams } from "./show-dialog-network";
import "../../../../src/components/sl-tab-group";
const IP_VERSIONS = ["ipv4", "ipv6"];
@@ -115,19 +116,19 @@ export class DialogHassioNetwork
></ha-icon-button>
</ha-header-bar>
${this._interfaces.length > 1
? html`<sl-tab-group @sl-tab-show=${this._handleTabActivated}
? html`<mwc-tab-bar
.activeIndex=${this._curTabIndex}
@MDCTabBar:activated=${this._handleTabActivated}
>${this._interfaces.map(
(device, index) =>
html`<sl-tab
slot="nav"
(device) =>
html`<mwc-tab
.id=${device.interface}
.panel=${index.toString()}
.active=${this._curTabIndex === index}
.label=${device.interface}
dialogInitialFocus
>
${device.interface}
</sl-tab>`
</mwc-tab>`
)}
</sl-tab-group>`
</mwc-tab-bar>`
: ""}
</div>
${cache(this._renderTab())}
@@ -160,20 +161,24 @@ export class DialogHassioNetwork
.disabled=${this._scanning}
>
${this._scanning
? html`<ha-spinner aria-label="Scanning" size="small">
</ha-spinner>`
? html`<ha-circular-progress
aria-label="Scanning"
indeterminate
size="small"
>
</ha-circular-progress>`
: this.supervisor.localize("dialog.network.scan_ap")}
</mwc-button>
${this._accessPoints &&
this._accessPoints.accesspoints &&
this._accessPoints.accesspoints.length !== 0
? html`
<ha-list>
<mwc-list>
${this._accessPoints.accesspoints
.filter((ap) => ap.ssid)
.map(
(ap) => html`
<ha-list-item
<mwc-list-item
twoline
@click=${this._selectAP}
.activated=${ap.ssid ===
@@ -188,10 +193,10 @@ export class DialogHassioNetwork
)}:
${ap.signal}
</span>
</ha-list-item>
</mwc-list-item>
`
)}
</ha-list>
</mwc-list>
`
: ""}
${this._wifiConfiguration
@@ -277,7 +282,8 @@ export class DialogHassioNetwork
</mwc-button>
<mwc-button @click=${this._updateNetwork} .disabled=${!this._dirty}>
${this._processing
? html`<ha-spinner size="small"> </ha-spinner>`
? html`<ha-circular-progress indeterminate size="small">
</ha-circular-progress>`
: this.supervisor.localize("common.save")}
</mwc-button>
</div>`;
@@ -484,8 +490,8 @@ export class DialogHassioNetwork
return;
}
}
this._curTabIndex = Number(ev.detail.name);
this._interface = { ...this._interfaces[this._curTabIndex] };
this._curTabIndex = ev.detail.index;
this._interface = { ...this._interfaces[ev.detail.index] };
}
private _handleRadioValueChanged(ev: CustomEvent): void {
@@ -559,6 +565,11 @@ export class DialogHassioNetwork
flex-shrink: 0;
}
mwc-tab-bar {
border-bottom: 1px solid
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
}
ha-dialog {
--dialog-content-position: static;
--dialog-content-padding: 0;
@@ -628,17 +639,9 @@ export class DialogHassioNetwork
ha-textfield {
padding: 0 14px;
}
ha-list-item {
mwc-list-item {
--mdc-list-side-padding: 10px;
}
sl-tab {
flex: 1;
}
sl-tab::part(base) {
width: 100%;
justify-content: center;
}
`,
];
}

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