20250430.0 (#25237)

This commit is contained in:
Paul Bottein 2025-04-30 13:10:20 +02:00 committed by GitHub
commit a699149388
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
571 changed files with 17909 additions and 10345 deletions

View File

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

View File

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

View File

@ -26,7 +26,7 @@ jobs:
- name: Check out files from GitHub - name: Check out files from GitHub
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.3.0 uses: actions/setup-node@v4.4.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@ -60,7 +60,7 @@ jobs:
- name: Check out files from GitHub - name: Check out files from GitHub
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.3.0 uses: actions/setup-node@v4.4.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@ -78,7 +78,7 @@ jobs:
- name: Check out files from GitHub - name: Check out files from GitHub
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.3.0 uses: actions/setup-node@v4.4.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@ -102,7 +102,7 @@ jobs:
- name: Check out files from GitHub - name: Check out files from GitHub
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.3.0 uses: actions/setup-node@v4.4.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn

View File

@ -27,7 +27,7 @@ jobs:
ref: dev ref: dev
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.3.0 uses: actions/setup-node@v4.4.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@ -42,9 +42,8 @@ jobs:
- name: Deploy to Netlify - name: Deploy to Netlify
id: deploy id: deploy
uses: netlify/actions/cli@master run: |
with: npx -y netlify-cli deploy --dir=demo/dist --prod
args: deploy --dir=demo/dist --prod
env: env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_DEV_SITE_ID }} NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_DEV_SITE_ID }}
@ -63,7 +62,7 @@ jobs:
ref: master ref: master
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.3.0 uses: actions/setup-node@v4.4.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@ -78,9 +77,8 @@ jobs:
- name: Deploy to Netlify - name: Deploy to Netlify
id: deploy id: deploy
uses: netlify/actions/cli@master run: |
with: npx -y netlify-cli deploy --dir=demo/dist --prod
args: deploy --dir=demo/dist --prod
env: env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_SITE_ID }} NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_SITE_ID }}

View File

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

View File

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

View File

@ -28,7 +28,7 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }} python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.3.0 uses: actions/setup-node@v4.4.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn

View File

@ -34,7 +34,7 @@ jobs:
uses: home-assistant/actions/helpers/verify-version@master uses: home-assistant/actions/helpers/verify-version@master
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.3.0 uses: actions/setup-node@v4.4.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@ -55,7 +55,7 @@ jobs:
script/release script/release
- name: Upload release assets - name: Upload release assets
uses: softprops/action-gh-release@v2.2.1 uses: softprops/action-gh-release@v2.2.2
with: with:
files: | files: |
dist/*.whl dist/*.whl
@ -74,7 +74,7 @@ jobs:
echo "home-assistant-frontend==$version" > ./requirements.txt echo "home-assistant-frontend==$version" > ./requirements.txt
- name: Build wheels - name: Build wheels
uses: home-assistant/wheels@2025.02.0 uses: home-assistant/wheels@2025.03.0
with: with:
abi: cp313 abi: cp313
tag: musllinux_1_2 tag: musllinux_1_2
@ -92,7 +92,7 @@ jobs:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.3.0 uses: actions/setup-node@v4.4.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@ -107,7 +107,7 @@ jobs:
- name: Tar folder - name: Tar folder
run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist . run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist .
- name: Upload release asset - name: Upload release asset
uses: softprops/action-gh-release@v2.2.1 uses: softprops/action-gh-release@v2.2.2
with: with:
files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz
@ -121,7 +121,7 @@ jobs:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.3.0 uses: actions/setup-node@v4.4.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@ -136,6 +136,6 @@ jobs:
- name: Tar folder - name: Tar folder
run: tar -czf hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz -C hassio/build . run: tar -czf hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz -C hassio/build .
- name: Upload release asset - name: Upload release asset
uses: softprops/action-gh-release@v2.2.1 uses: softprops/action-gh-release@v2.2.2
with: with:
files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz

View File

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

View File

@ -0,0 +1,22 @@
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

@ -0,0 +1,26 @@
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

@ -1,34 +0,0 @@
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++) {

File diff suppressed because one or more lines are too long

948
.yarn/releases/yarn-4.9.1.cjs vendored Executable file

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

@ -20,22 +20,16 @@ module.exports.ignorePackages = () => [];
// Files from NPM packages that we should replace with empty file // Files from NPM packages that we should replace with empty file
module.exports.emptyPackages = ({ isHassioBuild }) => module.exports.emptyPackages = ({ 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/typography.js"),
require.resolve("@vaadin/vaadin-material-styles/font-icons.js"), require.resolve("@vaadin/vaadin-material-styles/font-icons.js"),
// Icons in supervisor conflict with icons in HA so we don't load. // Icons in supervisor conflict with icons in HA so we don't load.
isHassioBuild && isHassioBuild &&
require.resolve( require.resolve(
path.resolve(paths.polymer_dir, "src/components/ha-icon.ts") path.resolve(paths.root_dir, "src/components/ha-icon.ts")
), ),
isHassioBuild && isHassioBuild &&
require.resolve( require.resolve(
path.resolve(paths.polymer_dir, "src/components/ha-icon-picker.ts") path.resolve(paths.root_dir, "src/components/ha-icon-picker.ts")
), ),
].filter(Boolean); ].filter(Boolean);
@ -50,7 +44,8 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
__HASS_URL__: `\`${ __HASS_URL__: `\`${
"HASS_URL" in process.env "HASS_URL" in process.env
? process.env.HASS_URL ? process.env.HASS_URL
: "${location.protocol}//${location.host}" : // eslint-disable-next-line no-template-curly-in-string
"${location.protocol}//${location.host}"
}\``, }\``,
"process.env.NODE_ENV": JSON.stringify( "process.env.NODE_ENV": JSON.stringify(
isProdBuild ? "production" : "development" isProdBuild ? "production" : "development"
@ -78,6 +73,19 @@ module.exports.terserOptions = ({ latestBuild, isTestBuild }) => ({
sourceMap: !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 = ({ module.exports.babelOptions = ({
latestBuild, latestBuild,
isProdBuild, isProdBuild,
@ -102,7 +110,6 @@ module.exports.babelOptions = ({
shippedProposals: true, shippedProposals: true,
}, },
], ],
"@babel/preset-typescript",
], ],
plugins: [ plugins: [
[ [
@ -139,12 +146,6 @@ module.exports.babelOptions = ({
"@babel/plugin-transform-runtime", "@babel/plugin-transform-runtime",
{ version: dependencies["@babel/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-class-properties",
"@babel/plugin-transform-private-methods", "@babel/plugin-transform-private-methods",
].filter(Boolean), ].filter(Boolean),
@ -164,7 +165,7 @@ module.exports.babelOptions = ({
], ],
], ],
exclude: [ exclude: [
path.join(paths.polymer_dir, "src/resources/polyfills"), path.join(paths.root_dir, "src/resources/polyfills"),
...[ ...[
"@formatjs/(?:ecma402-abstract|intl-\\w+)", "@formatjs/(?:ecma402-abstract|intl-\\w+)",
"@lit-labs/virtualizer/polyfills", "@lit-labs/virtualizer/polyfills",
@ -182,6 +183,7 @@ module.exports.babelOptions = ({
include: /\/node_modules\//, include: /\/node_modules\//,
exclude: [ exclude: [
"element-internals-polyfill", "element-internals-polyfill",
"@shoelace-style",
"@?lit(?:-labs|-element|-html)?", "@?lit(?:-labs|-element|-html)?",
].map((p) => new RegExp(`/node_modules/${p}/`)), ].map((p) => new RegExp(`/node_modules/${p}/`)),
}, },

View File

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

View File

@ -169,14 +169,14 @@ const APP_PAGE_ENTRIES = {
gulp.task( gulp.task(
"gen-pages-app-dev", "gen-pages-app-dev",
genPagesDevTask(APP_PAGE_ENTRIES, paths.polymer_dir, paths.app_output_root) genPagesDevTask(APP_PAGE_ENTRIES, paths.root_dir, paths.app_output_root)
); );
gulp.task( gulp.task(
"gen-pages-app-prod", "gen-pages-app-prod",
genPagesProdTask( genPagesProdTask(
APP_PAGE_ENTRIES, APP_PAGE_ENTRIES,
paths.polymer_dir, paths.root_dir,
paths.app_output_root, paths.app_output_root,
paths.app_output_latest, paths.app_output_latest,
paths.app_output_es5 paths.app_output_es5

View File

@ -6,8 +6,8 @@ import path from "path";
import paths from "../paths.cjs"; import paths from "../paths.cjs";
const npmPath = (...parts) => const npmPath = (...parts) =>
path.resolve(paths.polymer_dir, "node_modules", ...parts); path.resolve(paths.root_dir, "node_modules", ...parts);
const polyPath = (...parts) => path.resolve(paths.polymer_dir, ...parts); const polyPath = (...parts) => path.resolve(paths.root_dir, ...parts);
const copyFileDir = (fromFile, toDir) => const copyFileDir = (fromFile, toDir) =>
fs.copySync(fromFile, path.join(toDir, path.basename(fromFile))); fs.copySync(fromFile, path.join(toDir, path.basename(fromFile)));

View File

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

View File

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

View File

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

View File

@ -65,19 +65,26 @@ const createRspackConfig = ({
rules: [ rules: [
{ {
test: /\.m?js$|\.ts$/, test: /\.m?js$|\.ts$/,
use: (info) => ({ exclude: /node_modules[\\/]core-js/,
loader: "babel-loader", use: (info) => [
options: { {
...bundle.babelOptions({ loader: "babel-loader",
latestBuild, options: {
isProdBuild, ...bundle.babelOptions({
isTestBuild, latestBuild,
sw: info.issuerLayer === "sw", isProdBuild,
}), isTestBuild,
cacheDirectory: !isProdBuild, sw: info.issuerLayer === "sw",
cacheCompression: false, }),
cacheDirectory: !isProdBuild,
cacheCompression: false,
},
}, },
}), {
loader: "builtin:swc-loader",
options: bundle.swcOptions(),
},
],
resolve: { resolve: {
fullySpecified: false, fullySpecified: false,
}, },
@ -136,7 +143,8 @@ const createRspackConfig = ({
// calling define.amd will call require("!!webpack amd options") // calling define.amd will call require("!!webpack amd options")
resource.startsWith("!!webpack") || resource.startsWith("!!webpack") ||
// loaded by webpack dev server but doesn't exist. // loaded by webpack dev server but doesn't exist.
resource === "webpack/hot" resource === "webpack/hot" ||
resource.startsWith("@swc/helpers")
) { ) {
return false; return false;
} }
@ -161,7 +169,7 @@ const createRspackConfig = ({
}), }),
new rspack.NormalModuleReplacementPlugin( new rspack.NormalModuleReplacementPlugin(
new RegExp(bundle.emptyPackages({ isHassioBuild }).join("|")), new RegExp(bundle.emptyPackages({ isHassioBuild }).join("|")),
path.resolve(paths.polymer_dir, "src/util/empty.js") path.resolve(paths.root_dir, "src/util/empty.js")
), ),
!isProdBuild && new LogStartCompilePlugin(), !isProdBuild && new LogStartCompilePlugin(),
isProdBuild && isProdBuild &&

View File

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

View File

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

View File

@ -109,7 +109,7 @@ export class HcMain extends HassElement {
protected firstUpdated(changedProps) { protected firstUpdated(changedProps) {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
import("./hc-lovelace"); import("./hc-lovelace");
import("../../../../src/resources/ha-style"); import("../../../../src/resources/append-ha-style");
window.addEventListener("location-changed", () => { window.addEventListener("location-changed", () => {
const panelPath = `/${this._urlPath || "lovelace"}/`; const panelPath = `/${this._urlPath || "lovelace"}/`;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,26 +2,36 @@ import type { TodoItem } from "../../../src/data/todo";
import { TodoItemStatus } from "../../../src/data/todo"; import { TodoItemStatus } from "../../../src/data/todo";
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockTodo = (hass: MockHomeAssistant) => { const items = {
hass.mockWS("todo/item/list", () => ({ items: [
items: [ {
{ uid: "12",
uid: "12", summary: "Milk",
summary: "Milk", status: TodoItemStatus.NeedsAction,
status: TodoItemStatus.NeedsAction, },
}, {
{ uid: "13",
uid: "13", summary: "Eggs",
summary: "Eggs", status: TodoItemStatus.NeedsAction,
status: TodoItemStatus.NeedsAction, },
}, {
{ uid: "14",
uid: "14", summary: "Oranges",
summary: "Oranges", status: TodoItemStatus.Completed,
status: TodoItemStatus.Completed, },
}, {
] as TodoItem[], uid: "15",
})); summary: "Beer",
// eslint-disable-next-line @typescript-eslint/no-empty-function },
hass.mockWS("todo/item/subscribe", (_msg, _hass) => () => {}); ] 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 () => {};
});
}; };

View File

@ -42,7 +42,6 @@ export default tseslint.config(
__VERSION__: false, __VERSION__: false,
__STATIC_PATH__: false, __STATIC_PATH__: false,
__SUPERVISOR__: false, __SUPERVISOR__: false,
Polymer: true,
}, },
parser: tseslint.parser, parser: tseslint.parser,

View File

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

View File

@ -1,29 +1,30 @@
import type { TemplateResult } from "lit"; import type { TemplateResult } from "lit";
import { LitElement, html, css } from "lit"; import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators"; import { customElement, state } from "lit/decorators";
import "../../../../src/components/ha-formfield";
import { provideHass } from "../../../../src/fake_data/provide_hass"; import { provideHass } from "../../../../src/fake_data/provide_hass";
import type { HomeAssistant } from "../../../../src/types"; import type { HomeAssistant } from "../../../../src/types";
import "../../components/demo-black-white-row"; 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 { 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 { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
import type { Action } from "../../../../src/data/script";
import "../../../../src/panels/config/automation/action/ha-automation-action"; import "../../../../src/panels/config/automation/action/ha-automation-action";
import { HaChooseAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-choose"; 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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[] }[] = [ const SCHEMAS: { name: string; actions: Action[] }[] = [
{ name: "Event", actions: [HaEventAction.defaultConfig] }, { name: "Event", actions: [HaEventAction.defaultConfig] },

View File

@ -1,26 +1,27 @@
import type { TemplateResult } from "lit"; import type { TemplateResult } from "lit";
import { LitElement, html, css } from "lit"; import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators"; import { customElement, state } from "lit/decorators";
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 { 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 { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
import "../../../../src/components/ha-formfield";
import type { ConditionWithShorthand } from "../../../../src/data/automation"; import type { ConditionWithShorthand } from "../../../../src/data/automation";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../../../src/panels/config/automation/condition/ha-automation-condition"; 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 { 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 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 { 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 { 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 { 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 { 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 { 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 { HaZoneCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-zone";
import { HaAndCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-and"; import type { HomeAssistant } from "../../../../src/types";
import { HaOrCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-or"; import "../../components/demo-black-white-row";
import { HaNotCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-not";
const SCHEMAS: { name: string; conditions: ConditionWithShorthand[] }[] = [ const SCHEMAS: { name: string; conditions: ConditionWithShorthand[] }[] = [
{ {

View File

@ -1,35 +1,36 @@
import type { TemplateResult } from "lit"; import type { TemplateResult } from "lit";
import { LitElement, html, css } from "lit"; import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators"; import { customElement, state } from "lit/decorators";
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 { 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 { 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 type { Trigger } from "../../../../src/data/automation";
import { HaGeolocationTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-geo_location"; 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 { HaEventTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-event";
import { HaGeolocationTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-geo_location";
import { HaHassTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-homeassistant"; 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { HaZoneTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-zone";
import { HaDeviceTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-device"; import type { HomeAssistant } from "../../../../src/types";
import { HaStateTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-state"; import "../../components/demo-black-white-row";
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[] }[] = [ const SCHEMAS: { name: string; triggers: Trigger[] }[] = [
{ {

View File

@ -0,0 +1,65 @@
---
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

@ -0,0 +1,129 @@
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

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

View File

@ -18,7 +18,7 @@ Tooltips use `display: contents` so they won't interfere with how elements are p
## Documentation ## Documentation
This element is based on sholace `sl-tooltip` it only sets some css tokens and has a custom show/hide animation. 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> <a href="https://shoelace.style/components/tooltip" target="_blank" rel="noopener noreferrer">Shoelace documentation</a>
@ -28,3 +28,7 @@ In your theme settings use this without the prefixed `--`.
- `--ha-tooltip-border-radius` (Default: 4px) - `--ha-tooltip-border-radius` (Default: 4px)
- `--ha-tooltip-arrow-size` (Default: 8px) - `--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,9 +1,9 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit"; import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators"; import { customElement, state } from "lit/decorators";
import { formatDateTimeNumeric } from "../../../../src/common/datetime/format_date_time"; import { formatDateTimeNumeric } from "../../../../src/common/datetime/format_date_time";
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select"; import "../../../../src/components/ha-control-select";
import "../../../../src/components/ha-list";
import type { FrontendLocaleData } from "../../../../src/data/translation"; import type { FrontendLocaleData } from "../../../../src/data/translation";
import { import {
DateFormat, DateFormat,
@ -49,7 +49,7 @@ export class DemoDateTimeDateTimeNumeric extends LitElement {
@value-changed=${this.handleValueChanged} @value-changed=${this.handleValueChanged}
> >
</ha-control-select> </ha-control-select>
<mwc-list> <ha-list>
<div class="container header"> <div class="container header">
<div>Language</div> <div>Language</div>
<div class="center">Default (lang)</div> <div class="center">Default (lang)</div>
@ -96,7 +96,7 @@ export class DemoDateTimeDateTimeNumeric extends LitElement {
</div> </div>
` `
)} )}
</mwc-list> </ha-list>
`; `;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,11 @@
import type { PropertyValues, TemplateResult } from "lit"; import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit"; import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators"; 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 { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards"; 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 = [ const ENTITIES = [
getEntity("todo", "shopping_list", "2", { getEntity("todo", "shopping_list", "2", {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -52,6 +52,9 @@ class HassioAddonDashboard extends LitElement {
@property({ type: Boolean }) public narrow = false; @property({ type: Boolean }) public narrow = false;
@state()
private _controlEnabled = false;
@state() private _error?: string; @state() private _error?: string;
private _backPath = new URLSearchParams(window.parent.location.search).get( private _backPath = new URLSearchParams(window.parent.location.search).get(
@ -134,11 +137,17 @@ class HassioAddonDashboard extends LitElement {
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this.supervisor} .supervisor=${this.supervisor}
.addon=${this.addon} .addon=${this.addon}
.controlEnabled=${this._controlEnabled}
@system-managed-take-control=${this._enableControl}
></hassio-addon-router> ></hassio-addon-router>
</hass-tabs-subpage> </hass-tabs-subpage>
`; `;
} }
private _enableControl() {
this._controlEnabled = true;
}
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return [ return [
haStyle, haStyle,

View File

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

View File

@ -21,6 +21,9 @@ class HassioAddonInfoDashboard extends LitElement {
@property({ attribute: false }) public addon?: HassioAddonDetails; @property({ attribute: false }) public addon?: HassioAddonDetails;
@property({ type: Boolean, attribute: "control-enabled" })
public controlEnabled = false;
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.addon) { if (!this.addon) {
return html`<ha-spinner></ha-spinner>`; return html`<ha-spinner></ha-spinner>`;
@ -34,6 +37,7 @@ class HassioAddonInfoDashboard extends LitElement {
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this.supervisor} .supervisor=${this.supervisor}
.addon=${this.addon} .addon=${this.addon}
.controlEnabled=${this.controlEnabled}
></hassio-addon-info> ></hassio-addon-info>
</div> </div>
`; `;

View File

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

View File

@ -0,0 +1,60 @@
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

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import type { ActionDetail } from "@material/mwc-list"; import type { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiClose, mdiDotsVertical } from "@mdi/js"; import { mdiClose, mdiDotsVertical } from "@mdi/js";
import type { CSSResultGroup } from "lit"; import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit"; import { css, html, LitElement, nothing } from "lit";
@ -8,15 +8,17 @@ import { atLeastVersion } from "../../../../src/common/config/version";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import { stopPropagation } from "../../../../src/common/dom/stop_propagation"; import { stopPropagation } from "../../../../src/common/dom/stop_propagation";
import { slugify } from "../../../../src/common/string/slugify"; 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/buttons/ha-progress-button";
import "../../../../src/components/ha-alert"; import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-spinner";
import "../../../../src/components/ha-button"; import "../../../../src/components/ha-button";
import "../../../../src/components/ha-button-menu"; import "../../../../src/components/ha-button-menu";
import "../../../../src/components/ha-dialog-header";
import "../../../../src/components/ha-header-bar"; import "../../../../src/components/ha-header-bar";
import "../../../../src/components/ha-icon-button"; 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 { getSignedPath } from "../../../../src/data/auth";
import type { HassioBackupDetail } from "../../../../src/data/hassio/backup"; import type { HassioBackupDetail } from "../../../../src/data/hassio/backup";
import { import {
@ -36,7 +38,6 @@ import { fileDownload } from "../../../../src/util/file_download";
import "../../components/supervisor-backup-content"; import "../../components/supervisor-backup-content";
import type { SupervisorBackupContent } from "../../components/supervisor-backup-content"; import type { SupervisorBackupContent } from "../../components/supervisor-backup-content";
import type { HassioBackupDialogParams } from "./show-dialog-hassio-backup"; import type { HassioBackupDialogParams } from "./show-dialog-hassio-backup";
import type { HaMdDialog } from "../../../../src/components/ha-md-dialog";
@customElement("dialog-hassio-backup") @customElement("dialog-hassio-backup")
class HassioBackupDialog class HassioBackupDialog
@ -121,15 +122,15 @@ class HassioBackupDialog
.path=${mdiDotsVertical} .path=${mdiDotsVertical}
slot="trigger" slot="trigger"
></ha-icon-button> ></ha-icon-button>
<mwc-list-item <ha-list-item
>${this._dialogParams.supervisor.localize( >${this._dialogParams.supervisor.localize(
"backup.download_backup" "backup.download_backup"
)}</mwc-list-item )}</ha-list-item
> >
<mwc-list-item class="error" <ha-list-item class="error"
>${this._dialogParams.supervisor.localize( >${this._dialogParams.supervisor.localize(
"backup.delete_backup_title" "backup.delete_backup_title"
)}</mwc-list-item )}</ha-list-item
> >
</ha-button-menu>` </ha-button-menu>`
: nothing} : nothing}

View File

@ -1,12 +1,12 @@
import "@material/mwc-list/mwc-list-item";
import type { CSSResultGroup } from "lit"; import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit"; import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-spinner";
import "../../../../src/components/ha-select";
import "../../../../src/components/ha-dialog"; import "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-list-item";
import "../../../../src/components/ha-select";
import "../../../../src/components/ha-spinner";
import { import {
extractApiErrorMessage, extractApiErrorMessage,
ignoreSupervisorError, ignoreSupervisorError,
@ -95,8 +95,8 @@ class HassioDatadiskDialog extends LitElement {
> >
${this.devices.map( ${this.devices.map(
(device) => (device) =>
html`<mwc-list-item .value=${device} html`<ha-list-item .value=${device}
>${device}</mwc-list-item >${device}</ha-list-item
>` >`
)} )}
</ha-select> </ha-select>

View File

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

View File

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

View File

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

View File

@ -183,9 +183,6 @@ class HassioRepositoriesDialog extends LitElement {
ha-dialog.button-left { ha-dialog.button-left {
--justify-action-buttons: flex-start; --justify-action-buttons: flex-start;
} }
paper-icon-item {
cursor: pointer;
}
.form { .form {
color: var(--primary-text-color); color: var(--primary-text-color);
} }

View File

@ -0,0 +1,192 @@
import { mdiClose, mdiPuzzle, mdiSwapHorizontal } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { atLeastVersion } from "../../../../src/common/config/version";
import "../../../../src/components/ha-dialog-header";
import "../../../../src/components/ha-icon-button";
import "../../../../src/components/ha-icon-next";
import "../../../../src/components/ha-md-dialog";
import type { HaMdDialog } from "../../../../src/components/ha-md-dialog";
import "../../../../src/components/ha-md-list";
import "../../../../src/components/ha-md-list-item";
import "../../../../src/components/ha-svg-icon";
import {
getConfigEntry,
type ConfigEntry,
} from "../../../../src/data/config_entries";
import type { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import type { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
import { haStyle } from "../../../../src/resources/styles";
import type { HomeAssistant } from "../../../../src/types";
import { brandsUrl } from "../../../../src/util/brands-url";
import type { SystemManagedDialogParams } from "./show-dialog-system-managed";
@customElement("dialog-system-managed")
class HassioSystemManagedDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _supervisor?: Supervisor;
@state() private _addon?: HassioAddonDetails;
@state() private _open = false;
@state() private _configEntry?: ConfigEntry;
@query("ha-md-dialog") private _dialog?: HaMdDialog;
public async showDialog(
dialogParams: SystemManagedDialogParams
): Promise<void> {
this._addon = dialogParams.addon;
this._supervisor = dialogParams.supervisor;
this._open = true;
this._loadConfigEntry();
}
private _dialogClosed() {
this._addon = undefined;
this._supervisor = undefined;
this._configEntry = undefined;
this._open = false;
}
public closeDialog() {
this._dialog?.close();
return true;
}
protected render() {
if (!this._addon || !this._open || !this._supervisor) {
return nothing;
}
const addonImage =
atLeastVersion(this.hass.config.version, 0, 105) && this._addon.icon
? `/api/hassio/addons/${this._addon.slug}/icon`
: undefined;
return html`
<ha-md-dialog open @closed=${this._dialogClosed}>
<ha-dialog-header slot="headline">
<ha-icon-button
slot="navigationIcon"
.path=${mdiClose}
@click=${this.closeDialog}
></ha-icon-button>
<span slot="title">${this._addon?.name}</span>
</ha-dialog-header>
<div slot="content">
<div class="icons">
<ha-svg-icon
class="primary"
.path=${mdiHomeAssistant}
></ha-svg-icon>
<ha-svg-icon .path=${mdiSwapHorizontal}></ha-svg-icon>
${addonImage
? html`<img src=${addonImage} alt=${this._addon.name} />`
: html`<ha-svg-icon .path=${mdiPuzzle}></ha-svg-icon>`}
</div>
${this._supervisor.localize("addon.system_managed.title")}.<br />
${this._supervisor.localize("addon.system_managed.description")}
${this._configEntry
? html`
<h3>
${this._supervisor.localize(
"addon.system_managed.managed_by"
)}:
</h3>
<ha-md-list>
<ha-md-list-item
type="link"
href=${`/config/integrations/integration/${this._configEntry.domain}`}
>
<img
slot="start"
class="integration-icon"
alt=${this._configEntry.title}
src=${brandsUrl({
domain: this._configEntry.domain,
type: "icon",
darkOptimized: this.hass.themes?.darkMode,
})}
crossorigin="anonymous"
referrerpolicy="no-referrer"
@error=${this._onImageError}
@load=${this._onImageLoad}
/>
${this._configEntry.title}
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-md-list>
`
: nothing}
</div>
</ha-md-dialog>
`;
}
private _onImageLoad(ev) {
ev.target.style.visibility = "initial";
}
private _onImageError(ev) {
ev.target.style.visibility = "hidden";
}
private async _loadConfigEntry() {
if (this._addon?.system_managed_config_entry) {
try {
const { config_entry } = await getConfigEntry(
this.hass,
this._addon.system_managed_config_entry
);
this._configEntry = config_entry;
} catch (err) {
// eslint-disable-next-line no-console
console.error(err);
}
}
}
static get styles(): CSSResultGroup {
return [
haStyle,
css`
.icons {
display: flex;
justify-content: center;
align-items: center;
gap: 16px;
--mdc-icon-size: 48px;
margin-bottom: 32px;
}
.icons img {
width: 48px;
}
.icons .primary {
color: var(--primary-color);
}
.actions {
display: flex;
justify-content: space-between;
}
.integration-icon {
width: 24px;
}
ha-md-list-item {
--md-list-item-leading-space: 4px;
--md-list-item-trailing-space: 4px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-system-managed": HassioSystemManagedDialog;
}
}

View File

@ -0,0 +1,19 @@
import { fireEvent } from "../../../../src/common/dom/fire_event";
import type { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import type { Supervisor } from "../../../../src/data/supervisor/supervisor";
export interface SystemManagedDialogParams {
addon: HassioAddonDetails;
supervisor: Supervisor;
}
export const showSystemManagedDialog = (
element: HTMLElement,
dialogParams: SystemManagedDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-system-managed",
dialogImport: () => import("./dialog-system-managed"),
dialogParams,
});
};

View File

@ -1,9 +1,6 @@
import "./hassio-main"; import "./hassio-main";
import("../../src/resources/ha-style"); import("../../src/resources/append-ha-style");
import("@polymer/polymer/lib/utils/settings").then(
({ setCancelSyntheticClickEvents }) => setCancelSyntheticClickEvents(false)
);
const styleEl = document.createElement("style"); const styleEl = document.createElement("style");
styleEl.textContent = ` styleEl.textContent = `

View File

@ -12,12 +12,11 @@ export const hassioStyle = css`
h1 { h1 {
font-size: 2em; font-size: 2em;
margin-bottom: 8px; margin-bottom: 8px;
font-family: var(--paper-font-headline_-_font-family); font-family: var(--ha-font-family-body);
-webkit-font-smoothing: var(--paper-font-headline_-_-webkit-font-smoothing); -webkit-font-smoothing: var(--ha-font-smoothing);
font-size: var(--paper-font-headline_-_font-size); font-size: var(--ha-font-size-2xl);
font-weight: var(--paper-font-headline_-_font-weight); font-weight: var(--ha-font-weight-normal);
letter-spacing: var(--paper-font-headline_-_letter-spacing); line-height: var(--ha-line-height-condensed);
line-height: var(--paper-font-headline_-_line-height);
padding-left: 8px; padding-left: 8px;
padding-inline-start: 8px; padding-inline-start: 8px;
padding-inline-end: initial; padding-inline-end: initial;

View File

@ -1,5 +1,5 @@
import "@material/mwc-button"; import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import type { CSSResultGroup, TemplateResult } from "lit"; import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit"; import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
@ -197,9 +197,6 @@ class HassioCoreInfo extends LitElement {
color: var(--secondary-text-color); color: var(--secondary-text-color);
--mdc-menu-min-width: 200px; --mdc-menu-min-width: 200px;
} }
mwc-list-item ha-svg-icon {
color: var(--secondary-text-color);
}
a { a {
text-decoration: none; text-decoration: none;
} }

View File

@ -1,5 +1,5 @@
import "@material/mwc-button"; import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js"; import { mdiDotsVertical } from "@mdi/js";
import type { CSSResultGroup, TemplateResult } from "lit"; import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit"; import { css, html, LitElement } from "lit";
@ -10,6 +10,7 @@ import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/buttons/ha-progress-button"; import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-button-menu"; import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/components/ha-list-item";
import "../../../src/components/ha-icon-button"; import "../../../src/components/ha-icon-button";
import "../../../src/components/ha-settings-row"; import "../../../src/components/ha-settings-row";
import { import {
@ -188,31 +189,31 @@ class HassioHostInfo extends LitElement {
.path=${mdiDotsVertical} .path=${mdiDotsVertical}
slot="trigger" slot="trigger"
></ha-icon-button> ></ha-icon-button>
<mwc-list-item <ha-list-item
.action=${"hardware"} .action=${"hardware"}
@click=${this._handleMenuAction} @click=${this._handleMenuAction}
> >
${this.supervisor.localize("system.host.hardware")} ${this.supervisor.localize("system.host.hardware")}
</mwc-list-item> </ha-list-item>
${this.supervisor.host.features.includes("haos") ${this.supervisor.host.features.includes("haos")
? html` ? html`
<mwc-list-item <ha-list-item
.action=${"import_from_usb"} .action=${"import_from_usb"}
@click=${this._handleMenuAction} @click=${this._handleMenuAction}
> >
${this.supervisor.localize("system.host.import_from_usb")} ${this.supervisor.localize("system.host.import_from_usb")}
</mwc-list-item> </ha-list-item>
${this.supervisor.host.features.includes("os_agent") && ${this.supervisor.host.features.includes("os_agent") &&
atLeastVersion(this.supervisor.host.agent_version, 1, 2, 0) atLeastVersion(this.supervisor.host.agent_version, 1, 2, 0)
? html` ? html`
<mwc-list-item <ha-list-item
.action=${"move_datadisk"} .action=${"move_datadisk"}
@click=${this._handleMenuAction} @click=${this._handleMenuAction}
> >
${this.supervisor.localize( ${this.supervisor.localize(
"system.host.move_datadisk" "system.host.move_datadisk"
)} )}
</mwc-list-item> </ha-list-item>
` `
: ""} : ""}
` `
@ -438,7 +439,7 @@ class HassioHostInfo extends LitElement {
color: var(--secondary-text-color); color: var(--secondary-text-color);
--mdc-menu-min-width: 200px; --mdc-menu-min-width: 200px;
} }
mwc-list-item ha-svg-icon { ha-list-item ha-svg-icon {
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
a { a {

View File

@ -1,5 +1,5 @@
import "@material/mwc-button"; import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import type { CSSResultGroup, TemplateResult } from "lit"; import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit"; import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
@ -8,6 +8,7 @@ import "../../../src/components/ha-alert";
import "../../../src/components/ha-ansi-to-html"; import "../../../src/components/ha-ansi-to-html";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/components/ha-select"; import "../../../src/components/ha-select";
import "../../../src/components/ha-list-item";
import { extractApiErrorMessage } from "../../../src/data/hassio/common"; import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import { fetchHassioLogs } from "../../../src/data/hassio/supervisor"; import { fetchHassioLogs } from "../../../src/data/hassio/supervisor";
import type { Supervisor } from "../../../src/data/supervisor/supervisor"; import type { Supervisor } from "../../../src/data/supervisor/supervisor";
@ -80,9 +81,9 @@ class HassioSupervisorLog extends LitElement {
> >
${logProviders.map( ${logProviders.map(
(provider) => html` (provider) => html`
<mwc-list-item .value=${provider.key}> <ha-list-item .value=${provider.key}>
${provider.name} ${provider.name}
</mwc-list-item> </ha-list-item>
` `
)} )}
</ha-select> </ha-select>

View File

@ -1,4 +1,3 @@
import "@material/mwc-list/mwc-list-item";
import { import {
css, css,
type CSSResultGroup, type CSSResultGroup,

View File

@ -1,3 +1,3 @@
import "./ha-landing-page"; import "./ha-landing-page";
import("../../src/resources/ha-style"); import("../../src/resources/append-ha-style");

View File

@ -2,6 +2,8 @@ import "@material/mwc-linear-progress";
import { type PropertyValues, css, html, nothing } from "lit"; import { type PropertyValues, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import "../../src/components/ha-alert"; import "../../src/components/ha-alert";
import "../../src/components/ha-fade-in";
import "../../src/components/ha-spinner";
import { haStyle } from "../../src/resources/styles"; import { haStyle } from "../../src/resources/styles";
import "../../src/onboarding/onboarding-welcome-links"; import "../../src/onboarding/onboarding-welcome-links";
import "./components/landing-page-network"; import "./components/landing-page-network";
@ -40,6 +42,14 @@ class HaLandingPage extends LandingPageBaseElement {
render() { render() {
const networkIssue = this._networkInfo && !this._networkInfo.host_internet; const networkIssue = this._networkInfo && !this._networkInfo.host_internet;
if (!this.localize) {
return html`
<ha-fade-in>
<ha-spinner size="large"></ha-spinner>
</ha-fade-in>
`;
}
return html` return html`
<ha-card> <ha-card>
<div class="card-content"> <div class="card-content">
@ -229,6 +239,12 @@ class HaLandingPage extends LandingPageBaseElement {
margin-inline-end: 16px; margin-inline-end: 16px;
margin-inline-start: initial; margin-inline-start: initial;
} }
ha-fade-in {
min-height: calc(100vh - 64px - 88px);
display: flex;
justify-content: center;
align-items: center;
}
`, `,
]; ];
} }

View File

@ -6,23 +6,23 @@ import {
type LandingPageKeys, type LandingPageKeys,
type LocalizeFunc, type LocalizeFunc,
} from "../../src/common/translations/localize"; } from "../../src/common/translations/localize";
import { computeDirectionStyles } from "../../src/common/util/compute_rtl";
import { ProvideHassLitMixin } from "../../src/mixins/provide-hass-lit-mixin"; import { ProvideHassLitMixin } from "../../src/mixins/provide-hass-lit-mixin";
import { translationMetadata } from "../../src/resources/translations-metadata";
import type { HassBaseEl } from "../../src/state/hass-base-mixin";
import themesMixin from "../../src/state/themes-mixin";
import type { Constructor, Resources } from "../../src/types"; import type { Constructor, Resources } from "../../src/types";
import { import {
getLocalLanguage, getLocalLanguage,
getTranslation, getTranslation,
} from "../../src/util/common-translation"; } from "../../src/util/common-translation";
import { computeDirectionStyles } from "../../src/common/util/compute_rtl";
import themesMixin from "../../src/state/themes-mixin";
import { translationMetadata } from "../../src/resources/translations-metadata";
import type { HassBaseEl } from "../../src/state/hass-base-mixin";
export class LandingPageBaseElement extends themesMixin( export class LandingPageBaseElement extends themesMixin(
ProvideHassLitMixin(LitElement) as unknown as Constructor<HassBaseEl> ProvideHassLitMixin(LitElement) as unknown as Constructor<HassBaseEl>
) { ) {
// Initialized to empty will prevent undefined errors if called before connected to DOM. // Initialized to empty will prevent undefined errors if called before connected to DOM.
@property({ attribute: false }) @property({ attribute: false })
public localize: LocalizeFunc<LandingPageKeys> = () => ""; public localize?: LocalizeFunc<LandingPageKeys>;
// Use browser language setup before login. // Use browser language setup before login.
@property() public language?: string = getLocalLanguage(); @property() public language?: string = getLocalLanguage();

View File

@ -26,17 +26,17 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@babel/runtime": "7.26.10", "@babel/runtime": "7.27.0",
"@braintree/sanitize-url": "7.1.1", "@braintree/sanitize-url": "7.1.1",
"@codemirror/autocomplete": "6.18.6", "@codemirror/autocomplete": "6.18.6",
"@codemirror/commands": "6.8.0", "@codemirror/commands": "6.8.1",
"@codemirror/language": "6.11.0", "@codemirror/language": "6.11.0",
"@codemirror/legacy-modes": "6.5.0", "@codemirror/legacy-modes": "6.5.1",
"@codemirror/search": "6.5.10", "@codemirror/search": "6.5.10",
"@codemirror/state": "6.5.2", "@codemirror/state": "6.5.2",
"@codemirror/view": "6.36.4", "@codemirror/view": "6.36.6",
"@egjs/hammerjs": "2.0.17", "@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "6.17.4", "@formatjs/intl-datetimeformat": "6.18.0",
"@formatjs/intl-displaynames": "6.8.11", "@formatjs/intl-displaynames": "6.8.11",
"@formatjs/intl-durationformat": "0.7.4", "@formatjs/intl-durationformat": "0.7.4",
"@formatjs/intl-getcanonicallocales": "2.5.5", "@formatjs/intl-getcanonicallocales": "2.5.5",
@ -45,17 +45,18 @@
"@formatjs/intl-numberformat": "8.15.4", "@formatjs/intl-numberformat": "8.15.4",
"@formatjs/intl-pluralrules": "5.4.4", "@formatjs/intl-pluralrules": "5.4.4",
"@formatjs/intl-relativetimeformat": "11.4.11", "@formatjs/intl-relativetimeformat": "11.4.11",
"@fullcalendar/core": "6.1.15", "@fullcalendar/core": "6.1.17",
"@fullcalendar/daygrid": "6.1.15", "@fullcalendar/daygrid": "6.1.17",
"@fullcalendar/interaction": "6.1.15", "@fullcalendar/interaction": "6.1.17",
"@fullcalendar/list": "6.1.15", "@fullcalendar/list": "6.1.17",
"@fullcalendar/luxon3": "6.1.15", "@fullcalendar/luxon3": "6.1.17",
"@fullcalendar/timegrid": "6.1.15", "@fullcalendar/timegrid": "6.1.17",
"@lezer/highlight": "1.2.1", "@lezer/highlight": "1.2.1",
"@lit-labs/context": "0.4.1",
"@lit-labs/motion": "1.0.8", "@lit-labs/motion": "1.0.8",
"@lit-labs/observers": "2.0.5", "@lit-labs/observers": "2.0.5",
"@lit-labs/virtualizer": "2.1.0", "@lit-labs/virtualizer": "2.1.0",
"@lit/context": "1.1.5",
"@lit/reactive-element": "2.1.0",
"@material/chips": "=14.0.0-canary.53b3cad2f.0", "@material/chips": "=14.0.0-canary.53b3cad2f.0",
"@material/data-table": "=14.0.0-canary.53b3cad2f.0", "@material/data-table": "=14.0.0-canary.53b3cad2f.0",
"@material/mwc-base": "0.27.0", "@material/mwc-base": "0.27.0",
@ -65,36 +66,31 @@
"@material/mwc-drawer": "0.27.0", "@material/mwc-drawer": "0.27.0",
"@material/mwc-fab": "0.27.0", "@material/mwc-fab": "0.27.0",
"@material/mwc-floating-label": "0.27.0", "@material/mwc-floating-label": "0.27.0",
"@material/mwc-formfield": "0.27.0", "@material/mwc-formfield": "patch:@material/mwc-formfield@npm%3A0.27.0#~/.yarn/patches/@material-mwc-formfield-npm-0.27.0-9528cb60f6.patch",
"@material/mwc-icon-button": "0.27.0", "@material/mwc-icon-button": "0.27.0",
"@material/mwc-linear-progress": "0.27.0", "@material/mwc-linear-progress": "0.27.0",
"@material/mwc-list": "0.27.0", "@material/mwc-list": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
"@material/mwc-menu": "0.27.0", "@material/mwc-menu": "0.27.0",
"@material/mwc-radio": "0.27.0", "@material/mwc-radio": "0.27.0",
"@material/mwc-select": "0.27.0", "@material/mwc-select": "0.27.0",
"@material/mwc-snackbar": "0.27.0", "@material/mwc-snackbar": "0.27.0",
"@material/mwc-switch": "0.27.0", "@material/mwc-switch": "0.27.0",
"@material/mwc-tab": "0.27.0",
"@material/mwc-tab-bar": "0.27.0",
"@material/mwc-textarea": "0.27.0", "@material/mwc-textarea": "0.27.0",
"@material/mwc-textfield": "0.27.0", "@material/mwc-textfield": "0.27.0",
"@material/mwc-top-app-bar": "0.27.0", "@material/mwc-top-app-bar": "0.27.0",
"@material/mwc-top-app-bar-fixed": "0.27.0", "@material/mwc-top-app-bar-fixed": "0.27.0",
"@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0", "@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
"@material/web": "2.2.0", "@material/web": "2.3.0",
"@mdi/js": "7.4.47", "@mdi/js": "7.4.47",
"@mdi/svg": "7.4.47", "@mdi/svg": "7.4.47",
"@polymer/paper-item": "3.0.1",
"@polymer/paper-listbox": "3.0.1",
"@polymer/paper-tabs": "3.1.0",
"@polymer/polymer": "3.5.2",
"@replit/codemirror-indentation-markers": "6.5.3", "@replit/codemirror-indentation-markers": "6.5.3",
"@shoelace-style/shoelace": "2.20.1", "@shoelace-style/shoelace": "2.20.1",
"@swc/helpers": "0.5.17",
"@thomasloven/round-slider": "0.6.0", "@thomasloven/round-slider": "0.6.0",
"@tsparticles/engine": "3.8.1", "@tsparticles/engine": "3.8.1",
"@tsparticles/preset-links": "3.2.0", "@tsparticles/preset-links": "3.2.0",
"@vaadin/combo-box": "24.7.1", "@vaadin/combo-box": "24.7.4",
"@vaadin/vaadin-themable-mixin": "24.7.1", "@vaadin/vaadin-themable-mixin": "24.7.4",
"@vibrant/color": "4.0.0", "@vibrant/color": "4.0.0",
"@vue/web-component-wrapper": "1.3.0", "@vue/web-component-wrapper": "1.3.0",
"@webcomponents/scoped-custom-element-registry": "0.0.10", "@webcomponents/scoped-custom-element-registry": "0.0.10",
@ -111,22 +107,22 @@
"deep-freeze": "0.0.1", "deep-freeze": "0.0.1",
"dialog-polyfill": "0.5.6", "dialog-polyfill": "0.5.6",
"echarts": "5.6.0", "echarts": "5.6.0",
"element-internals-polyfill": "3.0.1", "element-internals-polyfill": "3.0.2",
"fuse.js": "7.1.0", "fuse.js": "7.1.0",
"google-timezones-json": "1.2.0", "google-timezones-json": "1.2.0",
"gulp-zopfli-green": "6.0.2", "gulp-zopfli-green": "6.0.2",
"hls.js": "patch:hls.js@npm%3A1.5.7#~/.yarn/patches/hls.js-npm-1.5.7-f5bbd3d060.patch", "hls.js": "patch:hls.js@npm%3A1.5.7#~/.yarn/patches/hls.js-npm-1.5.7-f5bbd3d060.patch",
"home-assistant-js-websocket": "9.4.0", "home-assistant-js-websocket": "9.5.0",
"idb-keyval": "6.2.1", "idb-keyval": "6.2.1",
"intl-messageformat": "10.7.16", "intl-messageformat": "10.7.16",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"leaflet": "1.9.4", "leaflet": "1.9.4",
"leaflet-draw": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch", "leaflet-draw": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch",
"leaflet.markercluster": "1.5.3", "leaflet.markercluster": "1.5.3",
"lit": "2.8.0", "lit": "3.3.0",
"lit-html": "2.8.0", "lit-html": "3.3.0",
"luxon": "3.5.0", "luxon": "3.6.1",
"marked": "15.0.7", "marked": "15.0.11",
"memoize-one": "6.0.0", "memoize-one": "6.0.0",
"node-vibrant": "4.0.3", "node-vibrant": "4.0.3",
"object-hash": "3.0.0", "object-hash": "3.0.0",
@ -156,18 +152,16 @@
"devDependencies": { "devDependencies": {
"@babel/core": "7.26.10", "@babel/core": "7.26.10",
"@babel/helper-define-polyfill-provider": "0.6.4", "@babel/helper-define-polyfill-provider": "0.6.4",
"@babel/plugin-proposal-decorators": "7.25.9",
"@babel/plugin-transform-runtime": "7.26.10", "@babel/plugin-transform-runtime": "7.26.10",
"@babel/preset-env": "7.26.9", "@babel/preset-env": "7.26.9",
"@babel/preset-typescript": "7.26.0",
"@bundle-stats/plugin-webpack-filter": "4.19.1", "@bundle-stats/plugin-webpack-filter": "4.19.1",
"@lokalise/node-api": "14.2.0", "@lokalise/node-api": "14.4.0",
"@octokit/auth-oauth-device": "7.1.4", "@octokit/auth-oauth-device": "7.1.5",
"@octokit/plugin-retry": "7.2.0", "@octokit/plugin-retry": "7.2.1",
"@octokit/rest": "21.1.1", "@octokit/rest": "21.1.1",
"@rsdoctor/rspack-plugin": "1.0.0", "@rsdoctor/rspack-plugin": "1.0.2",
"@rspack/cli": "1.2.8", "@rspack/cli": "1.3.7",
"@rspack/core": "1.2.8", "@rspack/core": "1.3.7",
"@types/babel__plugin-transform-runtime": "7.9.5", "@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.21", "@types/chromecast-caf-receiver": "6.0.21",
"@types/chromecast-caf-sender": "1.0.11", "@types/chromecast-caf-sender": "1.0.11",
@ -179,39 +173,39 @@
"@types/leaflet-draw": "1.0.11", "@types/leaflet-draw": "1.0.11",
"@types/leaflet.markercluster": "1.5.5", "@types/leaflet.markercluster": "1.5.5",
"@types/lodash.merge": "4.6.9", "@types/lodash.merge": "4.6.9",
"@types/luxon": "3.4.2", "@types/luxon": "3.6.2",
"@types/mocha": "10.0.10", "@types/mocha": "10.0.10",
"@types/qrcode": "1.5.5", "@types/qrcode": "1.5.5",
"@types/sortablejs": "1.15.8", "@types/sortablejs": "1.15.8",
"@types/tar": "6.1.13", "@types/tar": "6.1.13",
"@types/ua-parser-js": "0.7.39", "@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29", "@types/webspeechapi": "0.0.29",
"@vitest/coverage-v8": "3.0.9", "@vitest/coverage-v8": "3.1.2",
"babel-loader": "10.0.0", "babel-loader": "10.0.0",
"babel-plugin-template-html-minifier": "4.1.0", "babel-plugin-template-html-minifier": "4.1.0",
"browserslist-useragent-regexp": "4.1.3", "browserslist-useragent-regexp": "4.1.3",
"del": "8.0.0", "del": "8.0.0",
"eslint": "9.23.0", "eslint": "9.25.1",
"eslint-config-airbnb-base": "15.0.0", "eslint-config-airbnb-base": "15.0.0",
"eslint-config-prettier": "10.1.1", "eslint-config-prettier": "10.1.2",
"eslint-import-resolver-webpack": "0.13.10", "eslint-import-resolver-webpack": "0.13.10",
"eslint-plugin-import": "2.31.0", "eslint-plugin-import": "2.31.0",
"eslint-plugin-lit": "2.0.0", "eslint-plugin-lit": "2.1.1",
"eslint-plugin-lit-a11y": "4.1.4", "eslint-plugin-lit-a11y": "4.1.4",
"eslint-plugin-unused-imports": "4.1.4", "eslint-plugin-unused-imports": "4.1.4",
"eslint-plugin-wc": "3.0.0", "eslint-plugin-wc": "3.0.0",
"fancy-log": "2.0.0", "fancy-log": "2.0.0",
"fs-extra": "11.3.0", "fs-extra": "11.3.0",
"glob": "11.0.1", "glob": "11.0.2",
"gulp": "5.0.0", "gulp": "5.0.0",
"gulp-brotli": "3.0.0", "gulp-brotli": "3.0.0",
"gulp-json-transform": "0.5.0", "gulp-json-transform": "0.5.0",
"gulp-rename": "2.0.0", "gulp-rename": "2.0.0",
"html-minifier-terser": "7.2.0", "html-minifier-terser": "7.2.0",
"husky": "9.1.7", "husky": "9.1.7",
"jsdom": "26.0.0", "jsdom": "26.1.0",
"jszip": "3.10.1", "jszip": "3.10.1",
"lint-staged": "15.5.0", "lint-staged": "15.5.1",
"lit-analyzer": "2.0.3", "lit-analyzer": "2.0.3",
"lodash.merge": "4.6.2", "lodash.merge": "4.6.2",
"lodash.template": "4.5.0", "lodash.template": "4.5.0",
@ -220,29 +214,28 @@
"prettier": "3.5.3", "prettier": "3.5.3",
"rspack-manifest-plugin": "5.0.3", "rspack-manifest-plugin": "5.0.3",
"serve": "14.2.4", "serve": "14.2.4",
"sinon": "19.0.4", "sinon": "20.0.0",
"tar": "7.4.3", "tar": "7.4.3",
"terser-webpack-plugin": "5.3.14", "terser-webpack-plugin": "5.3.14",
"ts-lit-plugin": "2.0.2", "ts-lit-plugin": "2.0.2",
"typescript": "5.8.2", "typescript": "5.8.3",
"typescript-eslint": "8.27.0", "typescript-eslint": "8.31.0",
"vite-tsconfig-paths": "5.1.4", "vite-tsconfig-paths": "5.1.4",
"vitest": "3.0.9", "vitest": "3.1.2",
"webpack-stats-plugin": "1.1.3", "webpack-stats-plugin": "1.1.3",
"webpackbar": "7.0.0", "webpackbar": "7.0.0",
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch" "workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
}, },
"_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch",
"resolutions": { "resolutions": {
"@polymer/polymer": "patch:@polymer/polymer@3.5.2#./.yarn/patches/@polymer/polymer/pr-5569.patch",
"@material/mwc-button@^0.25.3": "^0.27.0", "@material/mwc-button@^0.25.3": "^0.27.0",
"lit": "2.8.0", "lit": "3.3.0",
"lit-html": "2.8.0", "lit-html": "3.3.0",
"clean-css": "5.3.3", "clean-css": "5.3.3",
"@lit/reactive-element": "1.6.3", "@lit/reactive-element": "2.1.0",
"@fullcalendar/daygrid": "6.1.15", "@fullcalendar/daygrid": "6.1.17",
"globals": "16.0.0", "globals": "16.0.0",
"tslib": "2.8.1" "tslib": "2.8.1",
"@material/mwc-list@^0.27.0": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch"
}, },
"packageManager": "yarn@4.7.0" "packageManager": "yarn@4.9.1"
} }

View File

@ -0,0 +1,17 @@
<svg width="94" height="24" viewBox="0 0 94 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1349_5726)">
<rect width="94" height="24" rx="8" fill="white"/>
<path d="M8 12C8 14.2091 9.79086 16 12 16H16C18.2091 16 20 14.2091 20 12C20 9.79086 18.2091 8 16 8H12C9.79086 8 8 9.79086 8 12Z" fill="black" fill-opacity="0.32"/>
<path d="M24.5 13C24.5 14.6569 25.8431 16 27.5 16H33.5C35.1569 16 36.5 14.6569 36.5 13V11C36.5 9.34315 35.1569 8 33.5 8H27.5C25.8431 8 24.5 9.34315 24.5 11V13Z" fill="black" fill-opacity="0.12"/>
<path d="M41 13C41 14.6569 42.3431 16 44 16H50C51.6569 16 53 14.6569 53 13V11C53 9.34315 51.6569 8 50 8H44C42.3431 8 41 9.34315 41 11V13Z" fill="black" fill-opacity="0.12"/>
<path d="M57.5 13C57.5 14.6569 58.8431 16 60.5 16H66.5C68.1569 16 69.5 14.6569 69.5 13V11C69.5 9.34315 68.1569 8 66.5 8H60.5C58.8431 8 57.5 9.34315 57.5 11V13Z" fill="black" fill-opacity="0.12"/>
<path d="M74 13C74 14.6569 75.3431 16 77 16H83C84.6569 16 86 14.6569 86 13V11C86 9.34315 84.6569 8 83 8H77C75.3431 8 74 9.34315 74 11V13Z" fill="black" fill-opacity="0.12"/>
<path d="M90 13C90 14.6569 91.3431 16 93 16H99C100.657 16 102 14.6569 102 13V11C102 9.34315 100.657 8 99 8H93C91.3431 8 90 9.34315 90 11V13Z" fill="black" fill-opacity="0.12"/>
</g>
<rect x="0.5" y="0.5" width="93" height="23" rx="7.5" stroke="black" stroke-opacity="0.12" stroke-dasharray="4 4"/>
<defs>
<clipPath id="clip0_1349_5726">
<rect width="94" height="24" rx="8" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,17 @@
<svg width="94" height="24" viewBox="0 0 94 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1349_5798)">
<path d="M0 8C0 3.58172 3.58172 0 8 0H86C90.4183 0 94 3.58172 94 8V16C94 20.4183 90.4183 24 86 24H8C3.58172 24 0 20.4183 0 16V8Z" fill="#1C1C1C"/>
<path d="M8 12C8 14.2091 9.79086 16 12 16H16C18.2091 16 20 14.2091 20 12C20 9.79086 18.2091 8 16 8H12C9.79086 8 8 9.79086 8 12Z" fill="white" fill-opacity="0.48"/>
<path d="M24.5 12C24.5 14.2091 26.2909 16 28.5 16H32.5C34.7091 16 36.5 14.2091 36.5 12C36.5 9.79086 34.7091 8 32.5 8H28.5C26.2909 8 24.5 9.79086 24.5 12Z" fill="white" fill-opacity="0.24"/>
<path d="M41 12C41 14.2091 42.7909 16 45 16H49C51.2091 16 53 14.2091 53 12C53 9.79086 51.2091 8 49 8H45C42.7909 8 41 9.79086 41 12Z" fill="white" fill-opacity="0.24"/>
<path d="M57.5 12C57.5 14.2091 59.2909 16 61.5 16H65.5C67.7091 16 69.5 14.2091 69.5 12C69.5 9.79086 67.7091 8 65.5 8H61.5C59.2909 8 57.5 9.79086 57.5 12Z" fill="white" fill-opacity="0.24"/>
<path d="M74 12C74 14.2091 75.7909 16 78 16H82C84.2091 16 86 14.2091 86 12C86 9.79086 84.2091 8 82 8H78C75.7909 8 74 9.79086 74 12Z" fill="white" fill-opacity="0.24"/>
<path d="M90 12C90 14.2091 91.7909 16 94 16H98C100.209 16 102 14.2091 102 12V12C102 9.79086 100.209 8 98 8H94C91.7909 8 90 9.79086 90 12V12Z" fill="white" fill-opacity="0.24"/>
</g>
<path d="M1.34748 20.4449C0.772837 19.5866 0.359906 18.6109 0.152272 17.5613L0.642766 17.4643C0.549158 16.9911 0.5 16.5015 0.5 16V14H0V10H0.5V8C0.5 7.49847 0.549158 7.00892 0.642766 6.53574L0.152272 6.4387C0.359906 5.38915 0.772837 4.41341 1.34748 3.55508L1.76296 3.83324C2.31067 3.01513 3.01513 2.31067 3.83323 1.76296L3.55507 1.34748C4.41341 0.772837 5.38915 0.359906 6.4387 0.152272L6.53574 0.642766C7.00892 0.549158 7.49847 0.5 8 0.5H9.94999V0H13.85V0.5H17.75V0H21.65V0.5H25.55V0H29.45V0.5H33.35V0H37.25V0.5H41.15V0H45.05V0.5H48.95V0H52.85V0.5H56.75V0H60.65V0.5H64.55V0H68.45V0.5H72.35V0H76.25V0.5H80.15V0H84.05V0.5H86C86.5015 0.5 86.9911 0.549158 87.4643 0.642766L87.5613 0.152273C88.6108 0.359907 89.5866 0.772837 90.4449 1.34747L90.1668 1.76296C90.9849 2.31067 91.6893 3.01513 92.237 3.83323L92.6525 3.55507C93.2272 4.41341 93.6401 5.38915 93.8477 6.4387L93.3572 6.53574C93.4508 7.00892 93.5 7.49847 93.5 8V10H94V14H93.5V16C93.5 16.5015 93.4508 16.9911 93.3572 17.4643L93.8477 17.5613C93.6401 18.6109 93.2272 19.5866 92.6525 20.4449L92.237 20.1668C91.6893 20.9849 90.9849 21.6893 90.1668 22.237L90.4449 22.6525C89.5866 23.2272 88.6108 23.6401 87.5613 23.8477L87.4643 23.3572C86.9911 23.4508 86.5015 23.5 86 23.5H84.05V24H80.15V23.5H76.25V24H72.35V23.5H68.45V24H64.55V23.5H60.65V24H56.75V23.5H52.85V24H48.95V23.5H45.05V24H41.15V23.5H37.25V24H33.35V23.5H29.45V24H25.55V23.5H21.65V24H17.75V23.5H13.85V24H9.95V23.5H8C7.49847 23.5 7.00892 23.4508 6.53574 23.3572L6.4387 23.8477C5.38915 23.6401 4.41341 23.2272 3.55507 22.6525L3.83323 22.237C3.01513 21.6893 2.31067 20.9849 1.76296 20.1668L1.34748 20.4449Z" stroke="white" stroke-opacity="0.24" stroke-dasharray="4 4"/>
<defs>
<clipPath id="clip0_1349_5798">
<path d="M0 8C0 3.58172 3.58172 0 8 0H86C90.4183 0 94 3.58172 94 8V16C94 20.4183 90.4183 24 86 24H8C3.58172 24 0 20.4183 0 16V8Z" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,11 @@
<svg width="94" height="40" viewBox="0 0 94 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="94" height="40" rx="8" fill="white"/>
<rect x="0.5" y="0.5" width="93" height="39" rx="7.5" stroke="black" stroke-opacity="0.12" stroke-dasharray="4 4"/>
<path d="M8 12C8 14.2091 9.79086 16 12 16H16C18.2091 16 20 14.2091 20 12C20 9.79086 18.2091 8 16 8H12C9.79086 8 8 9.79086 8 12Z" fill="black" fill-opacity="0.32"/>
<path d="M24.5 13C24.5 14.6569 25.8431 16 27.5 16H33.5C35.1569 16 36.5 14.6569 36.5 13V11C36.5 9.34315 35.1569 8 33.5 8H27.5C25.8431 8 24.5 9.34315 24.5 11V13Z" fill="black" fill-opacity="0.12"/>
<path d="M41 13C41 14.6569 42.3431 16 44 16H50C51.6569 16 53 14.6569 53 13V11C53 9.34315 51.6569 8 50 8H44C42.3431 8 41 9.34315 41 11V13Z" fill="black" fill-opacity="0.12"/>
<path d="M57.5 13C57.5 14.6569 58.8431 16 60.5 16H66.5C68.1569 16 69.5 14.6569 69.5 13V11C69.5 9.34315 68.1569 8 66.5 8H60.5C58.8431 8 57.5 9.34315 57.5 11V13Z" fill="black" fill-opacity="0.12"/>
<path d="M74 13C74 14.6569 75.3431 16 77 16H83C84.6569 16 86 14.6569 86 13V11C86 9.34315 84.6569 8 83 8H77C75.3431 8 74 9.34315 74 11V13Z" fill="black" fill-opacity="0.12"/>
<path d="M8 29C8 30.6569 9.34315 32 11 32H17C18.6569 32 20 30.6569 20 29V27C20 25.3431 18.6569 24 17 24H11C9.34315 24 8 25.3431 8 27V29Z" fill="black" fill-opacity="0.12"/>
<path d="M24.5 29C24.5 30.6569 25.8431 32 27.5 32H33.5C35.1569 32 36.5 30.6569 36.5 29V27C36.5 25.3431 35.1569 24 33.5 24H27.5C25.8431 24 24.5 25.3431 24.5 27V29Z" fill="black" fill-opacity="0.12"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,11 @@
<svg width="94" height="40" viewBox="0 0 94 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 8C0 3.58172 3.58172 0 8 0H86C90.4183 0 94 3.58172 94 8V32C94 36.4183 90.4183 40 86 40H8C3.58172 40 0 36.4183 0 32V8Z" fill="#1C1C1C"/>
<path d="M1.34748 36.4449C0.772837 35.5866 0.359906 34.6109 0.152272 33.5613L0.642766 33.4643C0.549158 32.9911 0.5 32.5015 0.5 32V30H0V26H0.5V22H0V18H0.5V14H0V10H0.5V8C0.5 7.49847 0.549158 7.00892 0.642766 6.53574L0.152272 6.4387C0.359906 5.38915 0.772837 4.41341 1.34748 3.55508L1.76296 3.83324C2.31067 3.01513 3.01513 2.31067 3.83323 1.76296L3.55507 1.34748C4.41341 0.772837 5.38915 0.359906 6.4387 0.152272L6.53574 0.642766C7.00892 0.549158 7.49847 0.5 8 0.5H9.94999V0H13.85V0.5H17.75V0H21.65V0.5H25.55V0H29.45V0.5H33.35V0H37.25V0.5H41.15V0H45.05V0.5H48.95V0H52.85V0.5H56.75V0H60.65V0.5H64.55V0H68.45V0.5H72.35V0H76.25V0.5H80.15V0H84.05V0.5H86C86.5015 0.5 86.9911 0.549158 87.4643 0.642766L87.5613 0.152273C88.6108 0.359907 89.5866 0.772837 90.4449 1.34747L90.1668 1.76296C90.9849 2.31067 91.6893 3.01513 92.237 3.83323L92.6525 3.55507C93.2272 4.41341 93.6401 5.38915 93.8477 6.4387L93.3572 6.53574C93.4508 7.00892 93.5 7.49847 93.5 8V10H94V14H93.5V18H94V22H93.5V26H94V30H93.5V32C93.5 32.5015 93.4508 32.9911 93.3572 33.4643L93.8477 33.5613C93.6401 34.6109 93.2272 35.5866 92.6525 36.4449L92.237 36.1668C91.6893 36.9849 90.9849 37.6893 90.1668 38.237L90.4449 38.6525C89.5866 39.2272 88.6108 39.6401 87.5613 39.8477L87.4643 39.3572C86.9911 39.4508 86.5015 39.5 86 39.5H84.05V40H80.15V39.5H76.25V40H72.35V39.5H68.45V40H64.55V39.5H60.65V40H56.75V39.5H52.85V40H48.95V39.5H45.05V40H41.15V39.5H37.25V40H33.35V39.5H29.45V40H25.55V39.5H21.65V40H17.75V39.5H13.85V40H9.95V39.5H8C7.49847 39.5 7.00892 39.4508 6.53574 39.3572L6.4387 39.8477C5.38915 39.6401 4.41341 39.2272 3.55508 38.6525L3.83323 38.237C3.01513 37.6893 2.31067 36.9849 1.76296 36.1668L1.34748 36.4449Z" stroke="white" stroke-opacity="0.24" stroke-dasharray="4 4"/>
<path d="M8 12C8 14.2091 9.79086 16 12 16H16C18.2091 16 20 14.2091 20 12C20 9.79086 18.2091 8 16 8H12C9.79086 8 8 9.79086 8 12Z" fill="white" fill-opacity="0.48"/>
<path d="M24.5 12C24.5 14.2091 26.2909 16 28.5 16H32.5C34.7091 16 36.5 14.2091 36.5 12C36.5 9.79086 34.7091 8 32.5 8H28.5C26.2909 8 24.5 9.79086 24.5 12Z" fill="white" fill-opacity="0.24"/>
<path d="M41 12C41 14.2091 42.7909 16 45 16H49C51.2091 16 53 14.2091 53 12C53 9.79086 51.2091 8 49 8H45C42.7909 8 41 9.79086 41 12Z" fill="white" fill-opacity="0.24"/>
<path d="M57.5 12C57.5 14.2091 59.2909 16 61.5 16H65.5C67.7091 16 69.5 14.2091 69.5 12C69.5 9.79086 67.7091 8 65.5 8H61.5C59.2909 8 57.5 9.79086 57.5 12Z" fill="white" fill-opacity="0.24"/>
<path d="M74 12C74 14.2091 75.7909 16 78 16H82C84.2091 16 86 14.2091 86 12C86 9.79086 84.2091 8 82 8H78C75.7909 8 74 9.79086 74 12Z" fill="white" fill-opacity="0.24"/>
<path d="M8 28C8 30.2091 9.79086 32 12 32H16C18.2091 32 20 30.2091 20 28C20 25.7909 18.2091 24 16 24H12C9.79086 24 8 25.7909 8 28Z" fill="white" fill-opacity="0.24"/>
<path d="M24.5 28C24.5 30.2091 26.2909 32 28.5 32H32.5C34.7091 32 36.5 30.2091 36.5 28C36.5 25.7909 34.7091 24 32.5 24H28.5C26.2909 24 24.5 25.7909 24.5 28Z" fill="white" fill-opacity="0.24"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,15 @@
<svg width="94" height="72" viewBox="0 0 94 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M76.9105 39.4999C77.739 39.4999 78.4105 38.8283 78.4105 37.9999C78.4105 37.1715 77.739 36.4999 76.9105 36.4999V39.4999ZM37.5 39.4999L76.9105 39.4999V36.4999L37.5 36.4999L37.5 39.4999Z" fill="#00AFFF" fill-opacity="0.3"/>
<path d="M30.8239 22.3365L38.8239 38.8365L30.3239 50.3365" stroke="black" stroke-opacity="0.12" stroke-width="3" stroke-linecap="round"/>
<mask id="mask0_1110_23734" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="30" y="27" width="18" height="18">
<path d="M45.75 42.075C45.75 42.4462 45.4462 42.75 45.075 42.75H32.925C32.5538 42.75 32.25 42.4462 32.25 42.075V36.675C32.25 36.3037 32.4649 35.7851 32.7276 35.5224L38.5224 29.7275C38.7851 29.4649 39.2143 29.4649 39.477 29.7275L45.2724 35.523C45.5351 35.7857 45.75 36.3043 45.75 36.6755V42.075Z" fill="black"/>
</mask>
<g mask="url(#mask0_1110_23734)">
<rect x="30" y="27" width="18" height="18" fill="#212121"/>
</g>
<path d="M82 37.9999C82 36.343 83.3431 34.9999 85 34.9999C86.6569 34.9999 88 36.343 88 37.9999C88 39.6567 86.6569 40.9999 85 40.9999C83.3431 40.9999 82 39.6567 82 37.9999Z" stroke="#00AFFF" stroke-width="2"/>
<rect x="23" y="11" width="8" height="8" rx="4" fill="black" fill-opacity="0.32"/>
<rect x="22" y="52" width="8" height="8" rx="4" fill="black" fill-opacity="0.32"/>
<path d="M21.5715 19.5C17.4983 23.801 15 29.6087 15 36C15 41.9085 17.1351 47.3183 20.6759 51.5" stroke="black" stroke-opacity="0.12" stroke-width="3" stroke-linecap="round"/>
<circle cx="39" cy="36" r="33.5" stroke="black" stroke-opacity="0.12"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,15 @@
<svg width="94" height="72" viewBox="0 0 94 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M30.824 22.3365L38.824 38.8365L30.324 50.3365" stroke="white" stroke-opacity="0.24" stroke-width="3" stroke-linecap="round"/>
<mask id="mask0_1180_4955" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="30" y="27" width="18" height="18">
<path d="M45.75 42.075C45.75 42.4462 45.4462 42.75 45.075 42.75H32.925C32.5538 42.75 32.25 42.4462 32.25 42.075V36.675C32.25 36.3037 32.4649 35.7851 32.7276 35.5224L38.5224 29.7275C38.7851 29.4649 39.2143 29.4649 39.477 29.7275L45.2724 35.523C45.5351 35.7857 45.75 36.3043 45.75 36.6755V42.075Z" fill="black"/>
</mask>
<g mask="url(#mask0_1180_4955)">
<rect x="30" y="27" width="18" height="18" fill="#00AFFF"/>
</g>
<path d="M76.9105 39.4999C77.739 39.4999 78.4105 38.8283 78.4105 37.9999C78.4105 37.1715 77.739 36.4999 76.9105 36.4999V39.4999ZM37.5 39.4999L76.9105 39.4999V36.4999L37.5 36.4999L37.5 39.4999Z" fill="#00AFFF" fill-opacity="0.3"/>
<path d="M82 37.9999C82 36.343 83.3431 34.9999 85 34.9999C86.6569 34.9999 88 36.343 88 37.9999C88 39.6567 86.6569 40.9999 85 40.9999C83.3431 40.9999 82 39.6567 82 37.9999Z" stroke="#00AFFF" stroke-width="2"/>
<rect x="23" y="11" width="8" height="8" rx="4" fill="white" fill-opacity="0.48"/>
<rect x="22" y="52" width="8" height="8" rx="4" fill="white" fill-opacity="0.48"/>
<path d="M21.5715 19.5C17.4983 23.801 15 29.6087 15 36C15 41.9085 17.1351 47.3183 20.6759 51.5" stroke="white" stroke-opacity="0.24" stroke-width="3" stroke-linecap="round"/>
<circle cx="39" cy="36" r="33.5" stroke="white" stroke-opacity="0.24"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,19 @@
<svg width="94" height="72" viewBox="0 0 94 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M63.1358 38.5084C63.9608 38.4334 64.5688 37.7037 64.4938 36.8787C64.4188 36.0537 63.6892 35.4457 62.8642 35.5207L63.1358 38.5084ZM46.6358 40.0084L63.1358 38.5084L62.8642 35.5207L46.3642 37.0207L46.6358 40.0084Z" fill="#00AFFF" fill-opacity="0.3"/>
<path d="M38.5 22L45.9722 37.4115C46.2967 38.0807 46.223 38.8747 45.781 39.4728L38 50" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
<circle cx="47" cy="36" r="34" fill="white"/>
<circle cx="47" cy="36" r="33.5" stroke="black" stroke-opacity="0.12"/>
<path d="M41.8777 12.5216C43.4905 12.1798 45.1631 12 46.8777 12C58.2401 12 67.7582 19.8959 70.2445 30.5M40 59C42.1788 59.6506 44.4874 60 46.8777 60C56.9498 60 65.5728 53.7955 69.1332 45" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
<path d="M38.5 22L45.9722 37.4115C46.2967 38.0807 46.223 38.8747 45.781 39.4728L38 50" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
<mask id="mask0_1110_23775" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="38" y="27" width="18" height="18">
<path d="M53.75 42.075C53.75 42.4462 53.4462 42.75 53.075 42.75H40.925C40.5538 42.75 40.25 42.4462 40.25 42.075V36.675C40.25 36.3037 40.4649 35.7851 40.7276 35.5224L46.5224 29.7275C46.7851 29.4649 47.2143 29.4649 47.477 29.7275L53.2724 35.523C53.5351 35.7857 53.75 36.3043 53.75 36.6755V42.075Z" fill="black"/>
</mask>
<g mask="url(#mask0_1110_23775)">
<rect x="38" y="27" width="18" height="18" fill="#212121"/>
</g>
<path d="M63.5 39.4999C64.3284 39.4999 65 38.8283 65 37.9999C65 37.1715 64.3284 36.4999 63.5 36.4999L63.5 39.4999ZM49.5 39.4999L63.5 39.4999L63.5 36.4999L49.5 36.4999L49.5 39.4999Z" fill="#00AFFF" fill-opacity="0.3"/>
<rect x="31" y="11" width="8" height="8" rx="4" fill="#00AFFF" fill-opacity="0.6"/>
<rect x="30" y="52" width="8" height="8" rx="4" fill="#00AFFF" fill-opacity="0.6"/>
<path d="M29.5715 19.5C25.4983 23.801 23 29.6087 23 36C23 41.9085 25.1351 47.3183 28.6759 51.5" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
<path d="M68 37.9999C68 36.343 69.3431 34.9999 71 34.9999C72.6569 34.9999 74 36.343 74 37.9999C74 39.6567 72.6569 40.9999 71 40.9999C69.3431 40.9999 68 39.6567 68 37.9999Z" stroke="#00AFFF" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,19 @@
<svg width="94" height="72" viewBox="0 0 94 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M63.1358 38.5084C63.9608 38.4334 64.5688 37.7037 64.4938 36.8787C64.4188 36.0537 63.6892 35.4457 62.8642 35.5207L63.1358 38.5084ZM46.6358 40.0084L63.1358 38.5084L62.8642 35.5207L46.3642 37.0207L46.6358 40.0084Z" fill="#00AFFF" fill-opacity="0.3"/>
<path d="M38.5 22L45.9722 37.4115C46.2967 38.0807 46.223 38.8747 45.781 39.4728L38 50" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
<circle cx="47" cy="36" r="34" fill="#1C1C1C"/>
<circle cx="47" cy="36" r="33.5" stroke="white" stroke-opacity="0.24"/>
<path d="M41.8777 12.5216C43.4905 12.1798 45.1631 12 46.8777 12C58.2401 12 67.7582 19.8959 70.2445 30.5M40 59C42.1788 59.6506 44.4874 60 46.8777 60C56.9498 60 65.5728 53.7955 69.1332 45" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
<path d="M38.5 22L45.9722 37.4115C46.2967 38.0807 46.223 38.8747 45.781 39.4728L38 50" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
<mask id="mask0_1180_4965" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="38" y="27" width="18" height="18">
<path d="M53.75 42.075C53.75 42.4462 53.4462 42.75 53.075 42.75H40.925C40.5538 42.75 40.25 42.4462 40.25 42.075V36.675C40.25 36.3037 40.4649 35.7851 40.7276 35.5224L46.5224 29.7275C46.7851 29.4649 47.2143 29.4649 47.477 29.7275L53.2724 35.523C53.5351 35.7857 53.75 36.3043 53.75 36.6755V42.075Z" fill="black"/>
</mask>
<g mask="url(#mask0_1180_4965)">
<rect x="38" y="27" width="18" height="18" fill="#00AFFF"/>
</g>
<path d="M63.5 39.4999C64.3284 39.4999 65 38.8283 65 37.9999C65 37.1715 64.3284 36.4999 63.5 36.4999L63.5 39.4999ZM49.5 39.4999L63.5 39.4999L63.5 36.4999L49.5 36.4999L49.5 39.4999Z" fill="#00AFFF" fill-opacity="0.3"/>
<rect x="31" y="11" width="8" height="8" rx="4" fill="#00AFFF" fill-opacity="0.6"/>
<rect x="30" y="52" width="8" height="8" rx="4" fill="#00AFFF" fill-opacity="0.6"/>
<path d="M29.5715 19.5C25.4983 23.801 23 29.6087 23 36C23 41.9085 25.1351 47.3183 28.6759 51.5" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
<path d="M68 37.9999C68 36.343 69.3431 34.9999 71 34.9999C72.6569 34.9999 74 36.343 74 37.9999C74 39.6567 72.6569 40.9999 71 40.9999C69.3431 40.9999 68 39.6567 68 37.9999Z" stroke="#00AFFF" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "home-assistant-frontend" name = "home-assistant-frontend"
version = "20250411.0" version = "20250430.0"
license = "Apache-2.0" license = "Apache-2.0"
license-files = ["LICENSE*"] license-files = ["LICENSE*"]
description = "The Home Assistant frontend" description = "The Home Assistant frontend"

View File

@ -265,7 +265,10 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
); );
} }
if (window.innerWidth > 450) { if (
window.innerWidth > 450 &&
!matchMedia("(prefers-reduced-motion)").matches
) {
import("../resources/particles"); import("../resources/particles");
} }

View File

@ -1,9 +1,9 @@
import "@material/mwc-list";
import { css, html, LitElement } from "lit"; import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import type { LocalizeFunc } from "../common/translations/localize"; import type { LocalizeFunc } from "../common/translations/localize";
import "../components/ha-icon-next"; import "../components/ha-icon-next";
import "../components/ha-list";
import "../components/ha-list-item"; import "../components/ha-list-item";
import type { AuthProvider } from "../data/auth"; import type { AuthProvider } from "../data/auth";
@ -29,7 +29,7 @@ export class HaPickAuthProvider extends LitElement {
>${this.localize("ui.panel.page-authorize.pick_auth_provider")}</span >${this.localize("ui.panel.page-authorize.pick_auth_provider")}</span
> >
</h3> </h3>
<mwc-list> <ha-list>
${this.authProviders.map( ${this.authProviders.map(
(provider) => html` (provider) => html`
<ha-list-item <ha-list-item
@ -43,7 +43,7 @@ export class HaPickAuthProvider extends LitElement {
</ha-list-item> </ha-list-item>
` `
)} )}
</mwc-list> </ha-list>
`; `;
} }
@ -77,7 +77,7 @@ export class HaPickAuthProvider extends LitElement {
background: var(--card-background-color); background: var(--card-background-color);
padding: 0 15px; padding: 0 15px;
} }
mwc-list { ha-list {
margin: 16px -16px 0; margin: 16px -16px 0;
--mdc-list-side-padding: 24px; --mdc-list-side-padding: 24px;
} }

View File

@ -144,7 +144,7 @@ export function theme2hex(themeColor: string): string {
return themeColor; return themeColor;
} }
const rgbFromColorName = colors[themeColor]; const rgbFromColorName = colors[themeColor.toLowerCase()];
if (rgbFromColorName) { if (rgbFromColorName) {
return rgb2hex(rgbFromColorName); return rgb2hex(rgbFromColorName);
} }

View File

@ -0,0 +1,102 @@
import type {
ReactiveController,
ReactiveControllerHost,
} from "@lit/reactive-element/reactive-controller";
import type { LitElement } from "lit";
/**
* The config options for a DragScrollController.
*/
export interface DragScrollControllerConfig {
selector: string;
}
export class DragScrollController implements ReactiveController {
public mouseIsDown = false;
public scrolled = false;
public scrolling = false;
public scrollStartX = 0;
public scrollLeft = 0;
private _host: ReactiveControllerHost & LitElement;
private _selector: string;
private _scrollContainer?: HTMLElement | null;
constructor(
host: ReactiveControllerHost & LitElement,
{ selector }: DragScrollControllerConfig
) {
this._selector = selector;
this._host = host;
host.addController(this);
}
hostUpdated() {
if (this._scrollContainer) {
return;
}
this._scrollContainer = this._host.renderRoot?.querySelector(
this._selector
);
if (this._scrollContainer) {
this._scrollContainer.addEventListener("mousedown", this._mouseDown);
}
}
hostDisconnected() {
window.removeEventListener("mousemove", this._mouseMove);
window.removeEventListener("mouseup", this._mouseUp);
}
private _mouseDown = (event: MouseEvent) => {
const scrollContainer = this._scrollContainer;
if (!scrollContainer) {
return;
}
this.scrollStartX = event.pageX - scrollContainer.offsetLeft;
this.scrollLeft = scrollContainer.scrollLeft;
this.mouseIsDown = true;
this.scrolled = false;
window.addEventListener("mousemove", this._mouseMove);
window.addEventListener("mouseup", this._mouseUp, { once: true });
};
private _mouseUp = () => {
this.mouseIsDown = false;
this.scrolling = false;
this._host.requestUpdate();
window.removeEventListener("mousemove", this._mouseMove);
};
private _mouseMove = (event: MouseEvent) => {
if (!this.mouseIsDown) {
return;
}
const scrollContainer = this._scrollContainer;
if (!scrollContainer) {
return;
}
const x = event.pageX - scrollContainer.offsetLeft;
const scroll = x - this.scrollStartX;
if (!this.scrolled) {
this.scrolled = Math.abs(scroll) > 1;
this.scrolling = this.scrolled;
this._host.requestUpdate();
}
scrollContainer.scrollLeft = this.scrollLeft - scroll;
};
}

View File

@ -84,12 +84,12 @@ export const calcDateRange = (
case "now-7d": case "now-7d":
return [ return [
calcDate(today, subDays, hass.locale, hass.config, 7), calcDate(today, subDays, hass.locale, hass.config, 7),
calcDate(today, subDays, hass.locale, hass.config, 1), calcDate(today, subDays, hass.locale, hass.config, 0),
]; ];
case "now-30d": case "now-30d":
return [ return [
calcDate(today, subDays, hass.locale, hass.config, 30), calcDate(today, subDays, hass.locale, hass.config, 30),
calcDate(today, subDays, hass.locale, hass.config, 1), calcDate(today, subDays, hass.locale, hass.config, 0),
]; ];
case "now-12m": case "now-12m":
return [ return [

View File

@ -1,46 +1,65 @@
import type { LitElement } from "lit"; import type { ReactiveElement } from "lit";
import type { ClassElement } from "../../types";
import { throttle } from "../util/throttle"; import { throttle } from "../util/throttle";
const throttleReplaceState = throttle((value) => { const throttleReplaceState = throttle((value) => {
history.replaceState({ scrollPosition: value }, ""); history.replaceState({ scrollPosition: value }, "");
}, 300); }, 300);
export const restoreScroll = export function restoreScroll(selector: string) {
(selector: string): any => return <ElemClass extends ReactiveElement>(
(element: ClassElement) => ({ proto: ElemClass,
kind: "method", propertyKey: string
placement: "prototype", ) => {
key: element.key, if (typeof propertyKey === "object") {
descriptor: { throw new Error("This decorator does not support this compilation type.");
set(this: LitElement, value: number) { }
throttleReplaceState(value);
this[`__${String(element.key)}`] = value; const connectedCallback = proto.connectedCallback;
}, proto.connectedCallback = function () {
get(this: LitElement) { connectedCallback.call(this);
return (
this[`__${String(element.key)}`] || history.state?.scrollPosition const scrollPos = this[propertyKey];
);
}, if (scrollPos) {
enumerable: true, this.updateComplete.then(() => {
configurable: true, const target = this.renderRoot.querySelector(selector);
}, if (!target) {
finisher(cls: typeof LitElement) { return;
const connectedCallback = cls.prototype.connectedCallback; }
cls.prototype.connectedCallback = function () { setTimeout(() => {
connectedCallback.call(this); target.scrollTop = scrollPos;
const scrollPos = this[element.key]; }, 0);
if (scrollPos) { });
this.updateComplete.then(() => { }
const target = this.renderRoot.querySelector(selector); };
if (!target) {
return; const descriptor = Object.getOwnPropertyDescriptor(proto, propertyKey);
} let newDescriptor: PropertyDescriptor;
setTimeout(() => { if (descriptor === undefined) {
target.scrollTop = scrollPos; newDescriptor = {
}, 0); get(this: ReactiveElement) {
}); return (
} this[`__${String(propertyKey)}`] || history.state?.scrollPosition
);
},
set(this: ReactiveElement, value) {
throttleReplaceState(value);
this[`__${String(propertyKey)}`] = value;
},
configurable: true,
enumerable: true,
}; };
}, } else {
}); const oldSetter = descriptor.set;
newDescriptor = {
...descriptor,
set(this: ReactiveElement, value) {
throttleReplaceState(value);
this[`__${String(propertyKey)}`] = value;
oldSetter?.call(this, value);
},
};
}
Object.defineProperty(proto, propertyKey, newDescriptor);
};
}

View File

@ -1,14 +1,18 @@
import type { UnsubscribeFunc } from "home-assistant-js-websocket"; import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { ReactiveElement } from "lit"; import { ReactiveElement } from "lit";
import type { InternalPropertyDeclaration } from "lit/decorators"; import type { InternalPropertyDeclaration } from "lit/decorators";
import type { ClassElement } from "../../types";
type Callback = (oldValue: any, newValue: any) => void; type Callback = (oldValue: any, newValue: any) => void;
type ReactiveStorageElement = ReactiveElement & {
__unbsubLocalStorage: UnsubscribeFunc | undefined;
__initialized: boolean;
};
class StorageClass { class StorageClass {
constructor(storage = window.localStorage) { constructor(store = window.localStorage) {
this.storage = storage; this.storage = store;
if (storage !== window.localStorage) { if (this.storage !== window.localStorage) {
// storage events only work for localStorage // storage events only work for localStorage
return; return;
} }
@ -99,17 +103,23 @@ class StorageClass {
const storages: Record<string, StorageClass> = {}; const storages: Record<string, StorageClass> = {};
export const storage = export function storage(options: {
(options: { key?: string;
key?: string; storage?: "localStorage" | "sessionStorage";
storage?: "localStorage" | "sessionStorage"; subscribe?: boolean;
subscribe?: boolean; state?: boolean;
state?: boolean; stateOptions?: InternalPropertyDeclaration;
stateOptions?: InternalPropertyDeclaration; serializer?: (value: any) => any;
serializer?: (value: any) => any; deserializer?: (value: any) => any;
deserializer?: (value: any) => any; }) {
}): any => return <ElemClass extends ReactiveElement>(
(clsElement: ClassElement) => { proto: ElemClass,
propertyKey: string
) => {
if (typeof propertyKey === "object") {
throw new Error("This decorator does not support this compilation type.");
}
const storageName = options.storage || "localStorage"; const storageName = options.storage || "localStorage";
let storageInstance: StorageClass; let storageInstance: StorageClass;
@ -120,11 +130,7 @@ export const storage =
storages[storageName] = storageInstance; storages[storageName] = storageInstance;
} }
const key = String(clsElement.key); const storageKey = options.key || String(propertyKey);
const storageKey = options.key || String(clsElement.key);
const initVal = clsElement.initializer
? clsElement.initializer()
: undefined;
storageInstance.addFromStorage(storageKey); storageInstance.addFromStorage(storageKey);
@ -134,7 +140,7 @@ export const storage =
storageInstance.subscribeChanges( storageInstance.subscribeChanges(
storageKey!, storageKey!,
(oldValue, _newValue) => { (oldValue, _newValue) => {
el.requestUpdate(clsElement.key, oldValue); el.requestUpdate(propertyKey, oldValue);
} }
) )
: undefined; : undefined;
@ -144,7 +150,7 @@ export const storage =
? options.deserializer ? options.deserializer
? options.deserializer(storageInstance.getValue(storageKey!)) ? options.deserializer(storageInstance.getValue(storageKey!))
: storageInstance.getValue(storageKey!) : storageInstance.getValue(storageKey!)
: initVal; : undefined;
const setValue = (el: ReactiveElement, value: any) => { const setValue = (el: ReactiveElement, value: any) => {
let oldValue: unknown | undefined; let oldValue: unknown | undefined;
@ -156,44 +162,74 @@ export const storage =
options.serializer ? options.serializer(value) : value options.serializer ? options.serializer(value) : value
); );
if (options.state) { if (options.state) {
el.requestUpdate(clsElement.key, oldValue); el.requestUpdate(propertyKey, oldValue);
} }
}; };
return { // @ts-ignore
kind: "method", const performUpdate = proto.performUpdate;
placement: "prototype", // @ts-ignore
key: clsElement.key, proto.performUpdate = function () {
descriptor: { (this as unknown as ReactiveStorageElement).__initialized = true;
set(this: ReactiveElement, value: unknown) { performUpdate.call(this);
setValue(this, value); };
},
get() { if (options.state && options.subscribe) {
const connectedCallback = proto.connectedCallback;
const disconnectedCallback = proto.disconnectedCallback;
proto.connectedCallback = function () {
connectedCallback.call(this);
const el = this as unknown as ReactiveStorageElement;
if (!el.__unbsubLocalStorage) {
el.__unbsubLocalStorage = subscribeChanges?.(this);
}
};
proto.disconnectedCallback = function () {
disconnectedCallback.call(this);
const el = this as unknown as ReactiveStorageElement;
el.__unbsubLocalStorage?.();
el.__unbsubLocalStorage = undefined;
};
}
if (options.state) {
ReactiveElement.createProperty(propertyKey, {
noAccessor: true,
...options.stateOptions,
});
}
const descriptor = Object.getOwnPropertyDescriptor(proto, propertyKey);
let newDescriptor: PropertyDescriptor;
if (descriptor === undefined) {
newDescriptor = {
get(this: ReactiveStorageElement) {
return getValue(); return getValue();
}, },
enumerable: true, set(this: ReactiveStorageElement, value) {
// Don't set the initial value if we have a value in localStorage
if (this.__initialized || getValue() === undefined) {
setValue(this, value);
this.requestUpdate(propertyKey, undefined);
}
},
configurable: true, configurable: true,
}, enumerable: true,
finisher(cls: typeof ReactiveElement) { };
if (options.state && options.subscribe) { } else {
const connectedCallback = cls.prototype.connectedCallback; const oldSetter = descriptor.set;
const disconnectedCallback = cls.prototype.disconnectedCallback; newDescriptor = {
cls.prototype.connectedCallback = function () { ...descriptor,
connectedCallback.call(this); set(this: ReactiveStorageElement, value) {
this[`__unbsubLocalStorage${key}`] = subscribeChanges?.(this); // Don't set the initial value if we have a value in localStorage
}; if (this.__initialized || getValue() === undefined) {
cls.prototype.disconnectedCallback = function () { setValue(this, value);
disconnectedCallback.call(this); this.requestUpdate(propertyKey, undefined);
this[`__unbsubLocalStorage${key}`]?.(); }
this[`__unbsubLocalStorage${key}`] = undefined; oldSetter?.call(this, value);
}; },
} };
if (options.state) { }
cls.createProperty(clsElement.key, { Object.defineProperty(proto, propertyKey, newDescriptor);
noAccessor: true,
...options.stateOptions,
});
}
},
};
}; };
}

View File

@ -1,39 +1,103 @@
import type { PropertyDeclaration, PropertyValues, ReactiveElement } from "lit"; import {
import type { ClassElement } from "../../types"; ReactiveElement,
type PropertyDeclaration,
type PropertyValues,
} from "lit";
import { shallowEqual } from "../util/shallow-equal"; import { shallowEqual } from "../util/shallow-equal";
/** /**
* Transform function type. * Transform function type.
*/ */
export type Transformer<T = any, V = any> = (value: V) => T; export type Transformer<T = any, V = any> = (value: T) => V | undefined;
type ReactiveTransformElement = ReactiveElement & { type ReactiveTransformElement = ReactiveElement & {
_transformers: Map<PropertyKey, Transformer>; _transformers: Map<PropertyKey, Transformer>;
_watching: Map<PropertyKey, Set<PropertyKey>>; _watching: Map<PropertyKey, Set<PropertyKey>>;
}; };
type ReactiveElementClassWithTransformers = typeof ReactiveElement & {
prototype: ReactiveTransformElement;
};
/** /**
* Specifies a transformer callback that is run when the value of the decorated property, or any of the properties in the watching array, changes. * Specifies a transformer callback that is run when the value of the decorated property, or any of the properties in the watching array, changes.
* The result of the transformer is assigned to the decorated property. * The result of the transformer is assigned to the decorated property.
* The transformer receives the current as argument. * The transformer receives the current as argument.
*/ */
export const transform = export function transform<T, V>(config: {
<T, V>(config: { transformer: Transformer<T, V>;
transformer: Transformer<T, V>; watch?: PropertyKey[];
watch?: PropertyKey[]; propertyOptions?: PropertyDeclaration;
propertyOptions?: PropertyDeclaration; }) {
}): any => return <ElemClass extends ReactiveElement>(
(clsElement: ClassElement) => { proto: ElemClass,
const key = String(clsElement.key); propertyKey: string
return { ) => {
...clsElement, if (typeof propertyKey === "object") {
kind: "method", throw new Error("This decorator does not support this compilation type.");
descriptor: { }
set(this: ReactiveTransformElement, value: V) {
const key = String(propertyKey);
const el = proto as unknown as ReactiveTransformElement;
// if we haven't wrapped `willUpdate` in this class, do so
if (!el._transformers) {
el._transformers = new Map<PropertyKey, Transformer>();
el._watching = new Map<PropertyKey, Set<PropertyKey>>();
// @ts-ignore
const userWillUpdate = el.willUpdate;
// @ts-ignore
el.willUpdate = function (
this: ReactiveTransformElement,
changedProperties: PropertyValues
) {
userWillUpdate.call(this, changedProperties);
const keys = new Set<PropertyKey>();
changedProperties.forEach((_v, k) => {
const watchers = this._watching;
const ks: Set<PropertyKey> | undefined = watchers.get(k);
if (ks !== undefined) {
ks.forEach((wk) => keys.add(wk));
}
});
keys.forEach((k) => {
// trigger setter
this[k] = this[`__original_${String(k)}`];
});
};
// clone any existing observers (superclasses)
// eslint-disable-next-line no-prototype-builtins
} else if (!el.hasOwnProperty("_transformers")) {
const tranformers = el._transformers;
el._transformers = new Map();
tranformers.forEach((v: any, k: PropertyKey) =>
el._transformers.set(k, v)
);
}
// set this method
el._transformers.set(propertyKey, config.transformer);
if (config.watch) {
// store watchers
config.watch.forEach((k) => {
let curWatch = el._watching.get(k);
if (!curWatch) {
curWatch = new Set();
el._watching.set(k, curWatch);
}
curWatch.add(propertyKey);
});
}
ReactiveElement.createProperty(propertyKey, {
noAccessor: true,
hasChanged: (v: any, o: any) => !shallowEqual(v, o),
...config.propertyOptions,
});
const descriptor = Object.getOwnPropertyDescriptor(proto, propertyKey);
let newDescriptor: PropertyDescriptor;
if (descriptor === undefined) {
newDescriptor = {
get(this: ReactiveTransformElement): V {
return this[`__transform_${key}`];
},
set(this: ReactiveTransformElement, value: T) {
const oldValue = this[`__transform_${key}`]; const oldValue = this[`__transform_${key}`];
const trnsformr: Transformer<T, V> | undefined = const trnsformr: Transformer<T, V> | undefined =
this._transformers.get(key); this._transformers.get(key);
@ -45,65 +109,28 @@ export const transform =
this[`__original_${key}`] = value; this[`__original_${key}`] = value;
this.requestUpdate(key, oldValue); this.requestUpdate(key, oldValue);
}, },
get(): T {
return this[`__transform_${key}`];
},
enumerable: true,
configurable: true, configurable: true,
}, enumerable: true,
finisher(cls: ReactiveElementClassWithTransformers) { };
// if we haven't wrapped `willUpdate` in this class, do so } else {
if (!cls.prototype._transformers) { const oldSetter = descriptor.set;
cls.prototype._transformers = new Map<PropertyKey, Transformer>(); newDescriptor = {
cls.prototype._watching = new Map<PropertyKey, Set<PropertyKey>>(); ...descriptor,
// @ts-ignore set(this: ReactiveTransformElement, value: T) {
const userWillUpdate = cls.prototype.willUpdate; const oldValue = this[`__transform_${key}`];
// @ts-ignore const trnsformr: Transformer | undefined =
cls.prototype.willUpdate = function ( this._transformers.get(key);
this: ReactiveTransformElement, if (trnsformr) {
changedProperties: PropertyValues this[`__transform_${key}`] = trnsformr.call(this, value);
) { } else {
userWillUpdate.call(this, changedProperties); this[`__transform_${key}`] = value;
const keys = new Set<PropertyKey>(); }
changedProperties.forEach((_v, k) => { this[`__original_${key}`] = value;
const watchers = this._watching; this.requestUpdate(key, oldValue);
const ks: Set<PropertyKey> | undefined = watchers.get(k); oldSetter?.call(this, value);
if (ks !== undefined) { },
ks.forEach((wk) => keys.add(wk)); };
} }
}); Object.defineProperty(proto, propertyKey, newDescriptor);
keys.forEach((k) => {
// trigger setter
this[k] = this[`__original_${String(k)}`];
});
};
// clone any existing observers (superclasses)
// eslint-disable-next-line no-prototype-builtins
} else if (!cls.prototype.hasOwnProperty("_transformers")) {
const tranformers = cls.prototype._transformers;
cls.prototype._transformers = new Map();
tranformers.forEach((v: any, k: PropertyKey) =>
cls.prototype._transformers.set(k, v)
);
}
// set this method
cls.prototype._transformers.set(clsElement.key, config.transformer);
if (config.watch) {
// store watchers
config.watch.forEach((k) => {
let curWatch = cls.prototype._watching.get(k);
if (!curWatch) {
curWatch = new Set();
cls.prototype._watching.set(k, curWatch);
}
curWatch.add(clsElement.key);
});
}
cls.createProperty(clsElement.key, {
noAccessor: true,
hasChanged: (v: any, o: any) => !shallowEqual(v, o),
...config.propertyOptions,
});
},
};
}; };
}

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