Compare commits

..

1 Commits

Author SHA1 Message Date
Paul Bottein
c080ebbf46 Add user selector with multiple and system option 2023-10-31 11:19:16 +01:00
1148 changed files with 28318 additions and 39224 deletions

View File

@@ -2,12 +2,12 @@
"name": "Home Assistant Frontend",
"build": {
"dockerfile": "Dockerfile",
"context": "..",
"context": ".."
},
"appPort": "8124:8123",
"postStartCommand": "script/bootstrap",
"containerEnv": {
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}",
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
},
"customizations": {
"vscode": {
@@ -16,7 +16,7 @@
"esbenp.prettier-vscode",
"runem.lit-plugin",
"github.vscode-pull-request-github",
"eamodio.gitlens",
"eamodio.gitlens"
],
"settings": {
"files.eol": "\n",
@@ -27,17 +27,17 @@
"editor.renderWhitespace": "boundary",
"editor.rulers": [80],
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"files.trimTrailingWhitespace": true,
"terminal.integrated.shell.linux": "/usr/bin/zsh",
"gitlens.showWelcomeOnInstall": false,
"gitlens.showWhatsNewAfterUpgrades": false,
"workbench.startupEditor": "none",
},
},
},
"workbench.startupEditor": "none"
}
}
}
}

View File

@@ -9,7 +9,7 @@ body:
If you have a feature or enhancement request for the frontend, please [start an discussion][fr] instead of creating an issue.
**Please do not report issues for custom cards.**
**Please not not report issues for custom cards.**
[fr]: https://github.com/home-assistant/frontend/discussions
[releases]: https://github.com/home-assistant/home-assistant/releases
@@ -24,7 +24,6 @@ body:
required: true
- label: I have tried a different browser to see if it is related to my browser.
required: true
- label: I have tried reproducing the issue in [safe mode](https://www.home-assistant.io/blog/2023/11/01/release-202311/#restarting-into-safe-mode) to rule out problems with unsupported custom resources.
- type: markdown
attributes:
value: |

56
.github/labeler.yml vendored
View File

@@ -1,51 +1,31 @@
Build:
- changed-files:
- any-glob-to-any-file:
- build-scripts/**
- .browserslistrc
- gulpfile.js
- build-scripts/**
- .browserslistrc
- gulpfile.js
Cast:
- changed-files:
- any-glob-to-any-file:
- cast/src/**
- src/cast/**
- cast/src/**
- src/cast/**
Demo:
- changed-files:
- any-glob-to-any-file:
- demo/src/**
- src/fake_data/**
- demo/src/**
- src/fake_data/**
Design:
- changed-files:
- any-glob-to-any-file:
- gallery/src/**
- src/fake_data/**
- gallery/src/**
- src/fake_data/**
Dependencies:
- any:
- changed-files:
# Match when only these files are changed (i.e. don't match PRs that happen to add or remove packages)
- any-glob-to-all-files:
- package.json
- renovate.json
- yarn.lock
- .yarn/**
- .yarnrc.yml
- .nvmrc
# Dependabot and Renovate branches always match (i.e. compatibility tweaks by members considered minor)
- head-branch:
- "^renovate/"
- "^dependabot/"
- package.json
- renovate.json
- yarn.lock
- .yarn/**
- .yarnrc.yml
- .nvmrc
GitHub Actions:
- changed-files:
- any-glob-to-any-file:
- .github/workflows/**
- .github/*.yml
- .github/workflows/**
- .github/*.yml
Supervisor:
- changed-files:
- any-glob-to-any-file:
- hassio/src/**
- hassio/src/**

View File

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

View File

@@ -26,7 +26,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -37,20 +37,17 @@ jobs:
- name: Build resources
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages
- name: Setup lint cache
uses: actions/cache@v4.0.0
uses: actions/cache@v3.3.2
with:
path: |
node_modules/.cache/prettier
node_modules/.cache/eslint
node_modules/.cache/typescript
key: lint-${{ github.sha }}
restore-keys: lint-
- name: Run eslint
run: yarn run lint:eslint --quiet
- name: Run tsc
run: yarn run lint:types
- name: Run lit-analyzer
run: yarn run lint:lit --quiet
- name: Run prettier
run: yarn run lint:prettier
test:
@@ -60,14 +57,14 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
- name: Install dependencies
run: yarn install --immutable
- name: Build resources
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data
run: ./node_modules/.bin/gulp build-translations build-locale-data
- name: Run Tests
run: yarn run test
build:
@@ -78,7 +75,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -89,7 +86,7 @@ jobs:
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@v4.3.0
uses: actions/upload-artifact@v3.1.3
with:
name: frontend-bundle-stats
path: build/stats/*.json
@@ -102,7 +99,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -113,7 +110,7 @@ jobs:
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@v4.3.0
uses: actions/upload-artifact@v3.1.3
with:
name: supervisor-bundle-stats
path: build/stats/*.json

View File

@@ -36,14 +36,14 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -57,4 +57,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v2

View File

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

View File

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

View File

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

View File

@@ -10,6 +10,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Apply labels
uses: actions/labeler@v5.0.0
uses: actions/labeler@v4.3.0
with:
sync-labels: true

View File

@@ -9,10 +9,9 @@ jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v5.0.1
- uses: dessant/lock-threads@v4.0.1
with:
github-token: ${{ github.token }}
process-only: "issues, prs"
issue-lock-inactive-days: "30"
issue-exclude-created-before: "2020-10-01T00:00:00Z"
issue-lock-reason: ""

View File

@@ -23,12 +23,12 @@ jobs:
uses: actions/checkout@v4.1.1
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -42,7 +42,7 @@ jobs:
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }}
- name: Bump version
run: script/version_bump.js nightly
run: script/version_bump.cjs nightly
- name: Build nightly Python wheels
run: |
@@ -57,14 +57,14 @@ jobs:
run: tar -czvf translations.tar.gz translations
- name: Upload build artifacts
uses: actions/upload-artifact@v4.3.0
uses: actions/upload-artifact@v3.1.3
with:
name: wheels
path: dist/home_assistant_frontend*.whl
if-no-files-found: error
- name: Upload translations
uses: actions/upload-artifact@v4.3.0
uses: actions/upload-artifact@v3.1.3
with:
name: translations
path: translations.tar.gz

View File

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

View File

@@ -29,12 +29,12 @@ jobs:
uses: home-assistant/actions/helpers/verify-version@master
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -74,7 +74,7 @@ jobs:
echo "home-assistant-frontend==$version" > ./requirements.txt
- name: Build wheels
uses: home-assistant/wheels@2024.01.0
uses: home-assistant/wheels@2023.10.5
with:
abi: cp311
tag: musllinux_1_2

View File

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

2
.nvmrc
View File

@@ -1 +1 @@
lts/iron
18

View File

@@ -0,0 +1,39 @@
diff --git a/modular/sortable.complete.esm.js b/modular/sortable.complete.esm.js
index 02e9f2d6bebeb430fe6e7c1cc3f9c3c9df051f14..bb8268b0844a1faa4108cc92c0be2a3dbaf23f83 100644
--- a/modular/sortable.complete.esm.js
+++ b/modular/sortable.complete.esm.js
@@ -1657,7 +1657,7 @@ Sortable.prototype =
target = parent; // store last element
}
/* jshint boss:true */
- while (parent = parent.parentNode);
+ while (parent = parent.parentNode || parent.getRootNode().host);
}
_unhideGhostForTarget();
diff --git a/modular/sortable.core.esm.js b/modular/sortable.core.esm.js
index b04c8b4634f7c6b4ef1aadbb48afe6564306dea9..39a107163c8c336ebd669b5ea8a936af87e1c1e7 100644
--- a/modular/sortable.core.esm.js
+++ b/modular/sortable.core.esm.js
@@ -1657,7 +1657,7 @@ Sortable.prototype =
target = parent; // store last element
}
/* jshint boss:true */
- while (parent = parent.parentNode);
+ while (parent = parent.parentNode || parent.getRootNode().host);
}
_unhideGhostForTarget();
diff --git a/modular/sortable.esm.js b/modular/sortable.esm.js
index 6ec7ed1bb557e21c2578200161e989c65d23150b..0a05475a22904472fac6c13f524c674da76584b0 100644
--- a/modular/sortable.esm.js
+++ b/modular/sortable.esm.js
@@ -1657,7 +1657,7 @@ Sortable.prototype =
target = parent; // store last element
}
/* jshint boss:true */
- while (parent = parent.parentNode);
+ while (parent = parent.parentNode || parent.getRootNode().host);
}
_unhideGhostForTarget();

View File

@@ -1,73 +0,0 @@
diff --git a/modular/sortable.core.esm.js b/modular/sortable.core.esm.js
index 93ba17509e2e8583ab241fea6845fbe714c584a2..de0651ddb5dced30d36f7d764da0dd0b441f523f 100644
--- a/modular/sortable.core.esm.js
+++ b/modular/sortable.core.esm.js
@@ -1461,7 +1461,7 @@ Sortable.prototype = /** @lends Sortable.prototype */{
}
target = parent; // store last element
}
- /* jshint boss:true */ while (parent = parent.parentNode);
+ /* jshint boss:true */ while (parent = parent.parentNode || parent.getRootNode().host);
}
_unhideGhostForTarget();
}
@@ -1781,11 +1781,16 @@ Sortable.prototype = /** @lends Sortable.prototype */{
}
if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, !!target) !== false) {
capture();
- if (elLastChild && elLastChild.nextSibling) {
- // the last draggable element is not the last node
- el.insertBefore(dragEl, elLastChild.nextSibling);
- } else {
- el.appendChild(dragEl);
+ try {
+ if (elLastChild && elLastChild.nextSibling) {
+ // the last draggable element is not the last node
+ el.insertBefore(dragEl, elLastChild.nextSibling);
+ } else {
+ el.appendChild(dragEl);
+ }
+ }
+ catch(err) {
+ return completed(false);
}
parentEl = el; // actualization
@@ -1802,7 +1807,13 @@ Sortable.prototype = /** @lends Sortable.prototype */{
targetRect = getRect(target);
if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, false) !== false) {
capture();
- el.insertBefore(dragEl, firstChild);
+ try {
+ el.insertBefore(dragEl, firstChild);
+ }
+ catch(err) {
+ return completed(false);
+ }
+
parentEl = el; // actualization
changed();
@@ -1849,12 +1860,17 @@ Sortable.prototype = /** @lends Sortable.prototype */{
_silent = true;
setTimeout(_unsilent, 30);
capture();
- if (after && !nextSibling) {
- el.appendChild(dragEl);
- } else {
- target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
- }
+ try {
+ if (after && !nextSibling) {
+ el.appendChild(dragEl);
+ } else {
+ target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
+ }
+ }
+ catch(err) {
+ return completed(false);
+ }
// Undo chrome's scroll adjustment (has no effect on other browsers)
if (scrolledPastTop) {
scrollBy(scrolledPastTop, 0, scrollBefore - scrolledPastTop.scrollTop);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

874
.yarn/releases/yarn-3.6.4.cjs vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,9 +1,11 @@
compressionLevel: mixed
defaultSemverRangePrefix: ""
enableGlobalCache: false
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.1.0.cjs
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
spec: "@yarnpkg/plugin-typescript"
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-3.6.4.cjs

View File

@@ -1,56 +0,0 @@
import defineProvider from "@babel/helper-define-polyfill-provider";
// List of polyfill keys with supported browser targets for the functionality
const PolyfillSupport = {
fetch: {
android: 42,
chrome: 42,
edge: 14,
firefox: 39,
ios: 10.3,
opera: 29,
opera_mobile: 29,
safari: 10.1,
samsung: 4.0,
},
proxy: {
android: 49,
chrome: 49,
edge: 12,
firefox: 18,
ios: 10.0,
opera: 36,
opera_mobile: 36,
safari: 10.0,
samsung: 5.0,
},
};
// Map of global variables and/or instance and static properties to the
// corresponding polyfill key and actual module to import
const polyfillMap = {
global: {
Proxy: { key: "proxy", module: "proxy-polyfill" },
fetch: { key: "fetch", module: "unfetch/polyfill" },
},
instance: {},
static: {},
};
// Create plugin using the same factory as for CoreJS
export default defineProvider(
({ createMetaResolver, debug, shouldInjectPolyfill }) => {
const resolvePolyfill = createMetaResolver(polyfillMap);
return {
name: "HA Custom",
polyfills: PolyfillSupport,
usageGlobal(meta, utils) {
const polyfill = resolvePolyfill(meta);
if (polyfill && shouldInjectPolyfill(polyfill.desc.key)) {
debug(polyfill.desc.key);
utils.injectGlobalImport(polyfill.desc.module);
}
},
};
}
);

View File

@@ -1,7 +1,6 @@
const path = require("path");
const env = require("./env.cjs");
const paths = require("./paths.cjs");
const { dependencies } = require("../package.json");
// GitHub base URL to use for production source maps
// Nightly builds use the commit SHA, otherwise assumes there is a tag that matches the version
@@ -13,7 +12,11 @@ module.exports.sourceMapURL = () => {
};
// Files from NPM Packages that should not be imported
module.exports.ignorePackages = () => [];
// eslint-disable-next-line unused-imports/no-unused-vars
module.exports.ignorePackages = ({ latestBuild }) => [
// Part of yaml.js and only used for !!js functions that we don't use
require.resolve("esprima"),
];
// Files from NPM packages that we should replace with empty file
module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
@@ -32,6 +35,8 @@ module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
require.resolve(
path.resolve(paths.polymer_dir, "src/resources/compatibility.ts")
),
// This polyfill is loaded in workers to support ES5, filter it out.
latestBuild && require.resolve("proxy-polyfill/src/index.js"),
// Icons in supervisor conflict with icons in HA so we don't load.
isHassioBuild &&
require.resolve(
@@ -86,12 +91,14 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
setSpreadProperties: true,
},
browserslistEnv: latestBuild ? "modern" : "legacy",
// Must be unambiguous because some dependencies are CommonJS only
sourceType: "unambiguous",
presets: [
[
"@babel/preset-env",
{
useBuiltIns: latestBuild ? false : "usage",
corejs: latestBuild ? false : dependencies["core-js"],
useBuiltIns: latestBuild ? false : "entry",
corejs: latestBuild ? false : { version: "3.33", proposals: true },
bugfixes: true,
shippedProposals: true,
},
@@ -109,39 +116,27 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
ignoreModuleNotFound: true,
},
],
[
path.resolve(
paths.polymer_dir,
"build-scripts/babel-plugins/custom-polyfill-plugin.js"
),
{ method: "usage-global" },
],
// Minify template literals for production
isProdBuild && [
"template-html-minifier",
{
modules: {
...Object.fromEntries(
["lit", "lit-element", "lit-html"].map((m) => [
m,
[
"html",
{ name: "svg", encapsulation: "svg" },
{ name: "css", encapsulation: "style" },
],
])
),
"@polymer/polymer/lib/utils/html-tag.js": ["html"],
lit: [
"html",
{ name: "svg", encapsulation: "svg" },
{ name: "css", encapsulation: "style" },
],
"@polymer/polymer/lib/utils/html-tag": ["html"],
},
strictCSS: true,
htmlMinifier: module.exports.htmlMinifierOptions,
failOnError: false, // we can turn this off in case of false positives
failOnError: true, // we can turn this off in case of false positives
},
],
// Import helpers and regenerator from runtime package
[
"@babel/plugin-transform-runtime",
{ version: dependencies["@babel/runtime"] },
{ version: require("../package.json").dependencies["@babel/runtime"] },
],
// Support some proposals still in TC39 process
["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],
@@ -152,18 +147,6 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
/node_modules[\\/]webpack[\\/]buildin/,
],
sourceMaps: !isTestBuild,
overrides: [
{
// Use unambiguous for dependencies so that require() is correctly injected into CommonJS files
// Exclusions are needed in some cases where ES modules have no static imports or exports, such as polyfills
sourceType: "unambiguous",
include: /\/node_modules\//,
exclude: [
"element-internals-polyfill",
"@?lit(?:-labs|-element|-html)?",
].map((p) => new RegExp(`/node_modules/${p}/`)),
},
],
});
const nameSuffix = (latestBuild) => (latestBuild ? "-modern" : "-legacy");

View File

@@ -30,8 +30,8 @@ gulp.task(
env.useWDS()
? "wds-watch-app"
: env.useRollup()
? "rollup-watch-app"
: "webpack-watch-app"
? "rollup-watch-app"
: "webpack-watch-app"
)
);

View File

@@ -8,10 +8,7 @@ const zopfliOptions = { threshold: 150 };
const compressDist = (rootDir) =>
gulp
.src([
`${rootDir}/**/*.{js,json,css,svg,xml}`,
`${rootDir}/{authorize,onboarding}.html`,
])
.src([`${rootDir}/**/*.{js,json,css,svg}`])
.pipe(zopfli(zopfliOptions))
.pipe(gulp.dest(rootDir));

View File

@@ -161,10 +161,6 @@ gulp.task("fetch-lokalise", async function () {
})
);
})
.catch((err) => {
console.error(err);
throw err;
})
)
);
});

View File

@@ -426,7 +426,6 @@ gulp.task(
"fetch-nightly-translations",
gulp.series("clean-translations", "ensure-translations-build-dir")
),
gulp.parallel("create-test-metadata", "create-test-translation"),
"build-master-translation",
"build-merged-translations",
"build-translation-fragment-supervisor",

View File

@@ -7,9 +7,6 @@ const TerserPlugin = require("terser-webpack-plugin");
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
const log = require("fancy-log");
const WebpackBar = require("webpackbar");
const {
TransformAsyncModulesPlugin,
} = require("transform-async-modules-webpack-plugin");
const paths = require("./paths.cjs");
const bundle = require("./bundle.cjs");
@@ -54,8 +51,8 @@ const createWebpackConfig = ({
devtool: isTestBuild
? false
: isProdBuild
? "nosources-source-map"
: "eval-cheap-module-source-map",
? "nosources-source-map"
: "eval-cheap-module-source-map",
entry,
node: false,
module: {
@@ -145,6 +142,17 @@ const createWebpackConfig = ({
),
path.resolve(paths.polymer_dir, "src/util/empty.js")
),
// See `src/resources/intl-polyfill-legacy.ts` for explanation
!latestBuild &&
new webpack.NormalModuleReplacementPlugin(
new RegExp(
path.resolve(paths.polymer_dir, "src/resources/intl-polyfill.ts")
),
path.resolve(
paths.polymer_dir,
"src/resources/intl-polyfill-legacy.ts"
)
),
!isProdBuild && new LogStartCompilePlugin(),
isProdBuild &&
new StatsWriterPlugin({
@@ -155,8 +163,6 @@ const createWebpackConfig = ({
stats: { assets: true, chunks: true, modules: true },
transform: (stats) => JSON.stringify(filterStats(stats)),
}),
!latestBuild &&
new TransformAsyncModulesPlugin({ browserslistEnv: "legacy" }),
].filter(Boolean),
resolve: {
extensions: [".ts", ".js", ".json"],
@@ -185,11 +191,11 @@ const createWebpackConfig = ({
filename: ({ chunk }) =>
!isProdBuild || isStatsBuild || dontHash.has(chunk.name)
? "[name].js"
: "[name].[contenthash].js",
: "[name]-[contenthash].js",
chunkFilename:
isProdBuild && !isStatsBuild ? "[name].[contenthash].js" : "[name].js",
isProdBuild && !isStatsBuild ? "[id]-[contenthash].js" : "[name].js",
assetModuleFilename:
isProdBuild && !isStatsBuild ? "[id].[contenthash][ext]" : "[id][ext]",
isProdBuild && !isStatsBuild ? "[id]-[contenthash][ext]" : "[id][ext]",
crossOriginLoading: "use-credentials",
hashFunction: "xxhash64",
hashDigest: "base64url",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -3,7 +3,7 @@ import { mdiCast, mdiCastConnected } from "@mdi/js";
import "@polymer/paper-item/paper-icon-item";
import "@polymer/paper-listbox/paper-listbox";
import { Auth, Connection } from "home-assistant-js-websocket";
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { CastManager } from "../../../../src/cast/cast_manager";
import {
@@ -22,27 +22,26 @@ import "../../../../src/components/ha-svg-icon";
import {
getLegacyLovelaceCollection,
getLovelaceCollection,
LovelaceConfig,
} from "../../../../src/data/lovelace";
import { isStrategyDashboard } from "../../../../src/data/lovelace/config/types";
import { LovelaceViewConfig } from "../../../../src/data/lovelace/config/view";
import "../../../../src/layouts/hass-loading-screen";
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
import "./hc-layout";
@customElement("hc-cast")
class HcCast extends LitElement {
@property({ attribute: false }) public auth!: Auth;
@property() public auth!: Auth;
@property({ attribute: false }) public connection!: Connection;
@property() public connection!: Connection;
@property({ attribute: false }) public castManager!: CastManager;
@property() public castManager!: CastManager;
@state() private askWrite = false;
@state() private lovelaceViews?: LovelaceViewConfig[] | null;
@state() private lovelaceConfig?: LovelaceConfig | null;
protected render(): TemplateResult {
if (this.lovelaceViews === undefined) {
if (this.lovelaceConfig === undefined) {
return html`<hass-loading-screen no-toolbar></hass-loading-screen>`;
}
@@ -73,44 +72,43 @@ class HcCast extends LitElement {
${error
? html` <div class="card-content">${error}</div> `
: !this.castManager.status
? html`
<p class="center-item">
<mwc-button raised @click=${this._handleLaunch}>
<ha-svg-icon .path=${mdiCast}></ha-svg-icon>
Start Casting
</mwc-button>
</p>
`
: html`
<div class="section-header">PICK A VIEW</div>
<paper-listbox
attr-for-selected="data-path"
.selected=${this.castManager.status.lovelacePath || ""}
>
${(
this.lovelaceViews ?? [
generateDefaultViewConfig({}, {}, {}, {}, () => ""),
]
).map(
(view, idx) => html`
<paper-icon-item
@click=${this._handlePickView}
data-path=${view.path || idx}
>
${view.icon
? html`
<ha-icon
.icon=${view.icon}
slot="item-icon"
></ha-icon>
`
: ""}
${view.title || view.path}
</paper-icon-item>
`
)}
</paper-listbox>
`}
? html`
<p class="center-item">
<mwc-button raised @click=${this._handleLaunch}>
<ha-svg-icon .path=${mdiCast}></ha-svg-icon>
Start Casting
</mwc-button>
</p>
`
: html`
<div class="section-header">PICK A VIEW</div>
<paper-listbox
attr-for-selected="data-path"
.selected=${this.castManager.status.lovelacePath || ""}
>
${(this.lovelaceConfig
? this.lovelaceConfig.views
: [generateDefaultViewConfig({}, {}, {}, {}, () => "")]
).map(
(view, idx) => html`
<paper-icon-item
@click=${this._handlePickView}
data-path=${view.path || idx}
>
${view.icon
? html`
<ha-icon
.icon=${view.icon}
slot="item-icon"
></ha-icon>
`
: ""}
${view.title || view.path}
</paper-icon-item>
`
)}
</paper-listbox>
`}
<div class="card-actions">
${this.castManager.status
? html`
@@ -138,15 +136,11 @@ class HcCast extends LitElement {
llColl.refresh().then(
() => {
llColl.subscribe((config) => {
if (isStrategyDashboard(config)) {
this.lovelaceViews = null;
} else {
this.lovelaceViews = config.views;
}
this.lovelaceConfig = config;
});
},
async () => {
this.lovelaceViews = null;
this.lovelaceConfig = null;
}
);
@@ -165,7 +159,9 @@ class HcCast extends LitElement {
toggleAttribute(
this,
"hide-icons",
this.lovelaceViews ? !this.lovelaceViews.some((view) => view.icon) : true
this.lovelaceConfig
? !this.lovelaceConfig.views.some((view) => view.icon)
: true
);
}
@@ -241,8 +237,6 @@ class HcCast extends LitElement {
mwc-button ha-svg-icon {
margin-right: 8px;
margin-inline-end: 8px;
margin-inline-start: initial;
height: 18px;
}

View File

@@ -1,5 +1,6 @@
import "@material/mwc-button";
import { mdiCastConnected, mdiCast } from "@mdi/js";
import "@polymer/paper-input/paper-input";
import {
Auth,
Connection,
@@ -23,7 +24,6 @@ import "../../../../src/components/ha-svg-icon";
import "../../../../src/layouts/hass-loading-screen";
import { registerServiceWorker } from "../../../../src/util/register-service-worker";
import "./hc-layout";
import "../../../../src/components/ha-textfield";
const seeFAQ = (qid) => html`
See <a href="./faq.html${qid ? `#${qid}` : ""}">the FAQ</a> for more
@@ -33,13 +33,13 @@ const translateErr = (err) =>
err === ERR_CANNOT_CONNECT
? "Unable to connect"
: err === ERR_HASS_HOST_REQUIRED
? "Please enter a Home Assistant URL."
: err === ERR_INVALID_HTTPS_TO_HTTP
? html`
Cannot connect to Home Assistant instances over "http://".
${seeFAQ("https")}
`
: `Unknown error (${err}).`;
? "Please enter a Home Assistant URL."
: err === ERR_INVALID_HTTPS_TO_HTTP
? html`
Cannot connect to Home Assistant instances over "http://".
${seeFAQ("https")}
`
: `Unknown error (${err}).`;
const INTRO = html`
<p>
@@ -116,11 +116,13 @@ export class HcConnect extends LitElement {
To get started, enter your Home Assistant URL and click authorize.
If you want a preview instead, click the show demo button.
</p>
<ha-textfield
label="Home Assistant URL"
placeholder="https://abcdefghijklmnop.ui.nabu.casa"
@keydown=${this._handleInputKeyDown}
></ha-textfield>
<p>
<paper-input
label="Home Assistant URL"
placeholder="https://abcdefghijklmnop.ui.nabu.casa"
@keydown=${this._handleInputKeyDown}
></paper-input>
</p>
${this.error ? html` <p class="error">${this.error}</p> ` : ""}
</div>
<div class="card-actions">
@@ -194,7 +196,7 @@ export class HcConnect extends LitElement {
}
private async _handleConnect() {
const inputEl = this.shadowRoot!.querySelector("ha-textfield")!;
const inputEl = this.shadowRoot!.querySelector("paper-input")!;
const value = inputEl.value || "";
this.error = undefined;
@@ -313,10 +315,6 @@ export class HcConnect extends LitElement {
.spacer {
flex: 1;
}
ha-textfield {
width: 100%;
}
`;
}
}

View File

@@ -10,13 +10,13 @@ import "../../../../src/components/ha-card";
@customElement("hc-layout")
class HcLayout extends LitElement {
@property() public subtitle?: string;
@property() public subtitle?: string | undefined;
@property({ attribute: false }) public auth?: Auth;
@property() public auth?: Auth;
@property({ attribute: false }) public connection?: Connection;
@property() public connection?: Connection;
@property({ attribute: false }) public user?: HassUser;
@property() public user?: HassUser;
protected render(): TemplateResult {
return html`

View File

@@ -1,5 +1,7 @@
import { LovelaceCardConfig } from "../../../../src/data/lovelace/config/card";
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
import {
LovelaceCardConfig,
LovelaceConfig,
} from "../../../../src/data/lovelace";
import { castContext } from "../cast_context";
export const castDemoLovelace: () => LovelaceConfig = () => {

View File

@@ -1,7 +1,7 @@
import { html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { mockHistory } from "../../../../demo/src/stubs/history";
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
import { LovelaceConfig } from "../../../../src/data/lovelace";
import {
MockHomeAssistant,
provideHass,

View File

@@ -12,8 +12,8 @@ class HcLaunchScreen extends LitElement {
return html`
<div class="container">
<img
alt="Nabu Casa logo on left, Home Assistant logo on right, and red heart in center"
src="https://cast.home-assistant.io/images/nabu-loves-hass.png"
alt="Home Assistant logo on left, Nabu Casa logo on right, and red heart in center"
src="https://www.home-assistant.io/images/blog/2018-09-thinking-big/social.png"
/>
<div class="status">
${this.hass ? "Connected" : "Not Connected"}
@@ -45,8 +45,6 @@ class HcLaunchScreen extends LitElement {
}
.status {
padding-right: 54px;
padding-inline-end: 54px;
padding-inline-start: initial;
}
`;
}

View File

@@ -1,7 +1,7 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
import { LovelaceConfig } from "../../../../src/data/lovelace";
import { Lovelace } from "../../../../src/panels/lovelace/types";
import "../../../../src/panels/lovelace/views/hui-view";
import { HomeAssistant } from "../../../../src/types";
@@ -14,8 +14,7 @@ import "./hc-launch-screen";
class HcLovelace extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false })
public lovelaceConfig!: LovelaceConfig;
@property({ attribute: false }) public lovelaceConfig!: LovelaceConfig;
@property() public viewPath?: string | number;

View File

@@ -21,26 +21,18 @@ import {
import { atLeastVersion } from "../../../../src/common/config/version";
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
import {
fetchResources,
getLegacyLovelaceCollection,
getLovelaceCollection,
} from "../../../../src/data/lovelace";
import {
isStrategyDashboard,
LegacyLovelaceConfig,
LovelaceConfig,
LovelaceDashboardStrategyConfig,
} from "../../../../src/data/lovelace/config/types";
import { fetchResources } from "../../../../src/data/lovelace/resource";
} from "../../../../src/data/lovelace";
import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/load-resources";
import { HassElement } from "../../../../src/state/hass-element";
import { castContext } from "../cast_context";
import "./hc-launch-screen";
const DEFAULT_CONFIG: LovelaceDashboardStrategyConfig = {
strategy: {
type: "original-states",
},
};
const DEFAULT_STRATEGY = "original-states";
let resourcesLoaded = false;
@customElement("hc-main")
@@ -101,7 +93,7 @@ export class HcMain extends HassElement {
.lovelaceConfig=${this._lovelaceConfig}
.viewPath=${this._lovelacePath}
.urlPath=${this._urlPath}
@config-refresh=${this._generateDefaultLovelaceConfig}
@config-refresh=${this._generateLovelaceConfig}
></hc-lovelace>
`;
}
@@ -205,6 +197,7 @@ export class HcMain extends HassElement {
expires_in: 0,
}),
});
this._hassUUID = msg.hassUUID;
} catch (err: any) {
const errorMessage = this._getErrorMessage(err);
this._error = errorMessage;
@@ -224,17 +217,6 @@ export class HcMain extends HassElement {
this.hass.connection.close();
}
this.initializeHass(auth, connection);
if (this._hassUUID !== msg.hassUUID) {
this._hassUUID = msg.hassUUID;
this._lovelacePath = null;
this._urlPath = undefined;
this._lovelaceConfig = undefined;
if (this._unsubLovelace) {
this._unsubLovelace();
this._unsubLovelace = undefined;
}
resourcesLoaded = false;
}
this._error = undefined;
this._sendStatus();
}
@@ -243,7 +225,7 @@ export class HcMain extends HassElement {
this._showDemo = false;
// We should not get this command before we are connected.
// Means a client got out of sync. Let's send status to them.
if (!this.hass?.connected) {
if (!this.hass) {
this._sendStatus(msg.senderId!);
this._error = "Cannot show Lovelace because we're not connected.";
this._sendError(
@@ -294,7 +276,6 @@ export class HcMain extends HassElement {
this._lovelaceConfig = undefined;
if (this._unsubLovelace) {
this._unsubLovelace();
this._unsubLovelace = undefined;
}
const llColl = atLeastVersion(this.hass.connection.haVersion, 0, 107)
? getLovelaceCollection(this.hass.connection, msg.urlPath)
@@ -303,20 +284,9 @@ export class HcMain extends HassElement {
// configuration.
try {
await llColl.refresh();
this._unsubLovelace = llColl.subscribe(async (rawConfig) => {
if (isStrategyDashboard(rawConfig)) {
const { generateLovelaceDashboardStrategy } = await import(
"../../../../src/panels/lovelace/strategies/get-strategy"
);
const config = await generateLovelaceDashboardStrategy(
rawConfig.strategy,
this.hass!
);
this._handleNewLovelaceConfig(config);
} else {
this._handleNewLovelaceConfig(rawConfig);
}
});
this._unsubLovelace = llColl.subscribe((lovelaceConfig) =>
this._handleNewLovelaceConfig(lovelaceConfig)
);
} catch (err: any) {
if (
atLeastVersion(this.hass.connection.haVersion, 0, 107) &&
@@ -330,7 +300,7 @@ export class HcMain extends HassElement {
}
// Generate a Lovelace config.
this._unsubLovelace = () => undefined;
await this._generateDefaultLovelaceConfig();
await this._generateLovelaceConfig();
}
}
if (!resourcesLoaded) {
@@ -346,13 +316,15 @@ export class HcMain extends HassElement {
this._sendStatus();
}
private async _generateDefaultLovelaceConfig() {
private async _generateLovelaceConfig() {
const { generateLovelaceDashboardStrategy } = await import(
"../../../../src/panels/lovelace/strategies/get-strategy"
);
this._handleNewLovelaceConfig(
await generateLovelaceDashboardStrategy(
DEFAULT_CONFIG.strategy,
{
type: DEFAULT_STRATEGY,
},
this.hass!
)
);

View File

@@ -1,5 +1,5 @@
import { LocalizeFunc } from "../../../src/common/translations/localize";
import { LovelaceConfig } from "../../../src/data/lovelace/config/types";
import { LovelaceConfig } from "../../../src/data/lovelace";
import { Entity } from "../../../src/fake_data/entity";
export interface DemoConfig {

View File

@@ -4,7 +4,7 @@ import { customElement, property, state } from "lit/decorators";
import { until } from "lit/directives/until";
import "../../../src/components/ha-card";
import "../../../src/components/ha-circular-progress";
import { LovelaceCardConfig } from "../../../src/data/lovelace/config/card";
import { LovelaceCardConfig } from "../../../src/data/lovelace";
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
import { Lovelace, LovelaceCard } from "../../../src/panels/lovelace/types";
import {
@@ -39,9 +39,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
<div class="picker">
<div class="label">
${this._switching
? html`<ha-circular-progress
indeterminate
></ha-circular-progress>`
? html`<ha-circular-progress active></ha-circular-progress>`
: until(
selectedDemoConfig.then(
(conf) => html`
@@ -50,7 +48,8 @@ export class HADemoCard extends LitElement implements LovelaceCard {
<a target="_blank" href=${conf.authorUrl}>
${this.hass.localize(
"ui.panel.page-demo.cards.demo.demo_by",
{ name: conf.authorName }
"name",
conf.authorName
)}
</a>
</small>

View File

@@ -74,9 +74,19 @@
<body>
<div id="ha-launch-screen">
<div class="ha-launch-screen-spacer"></div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
<path fill="#18BCF2" d="M240 224.762a15 15 0 0 1-15 15H15a15 15 0 0 1-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.7805c5.83-5.83 15.38-5.83 21.21 0l98.79 98.7895c5.83 5.83 10.61 17.36 10.61 25.61v90-.01Z"/>
<path fill="#F2F4F9" d="m107.27 239.762-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.3395-11.5-10.3195-11.5-18.3895 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.3895v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88z"/>
<svg
viewBox="0 0 240 240"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M240 224.762C240 233.012 233.25 239.762 225 239.762H15C6.75 239.762 0 233.012 0 224.762V134.762C0 126.512 4.77 114.993 10.61 109.153L109.39 10.3725C115.22 4.5425 124.77 4.5425 130.6 10.3725L229.39 109.162C235.22 114.992 240 126.522 240 134.772V224.772V224.762Z"
fill="#F2F4F9"
/>
<path
d="M229.39 109.153L130.61 10.3725C124.78 4.5425 115.23 4.5425 109.4 10.3725L10.61 109.153C4.78 114.983 0 126.512 0 134.762V224.762C0 233.012 6.75 239.762 15 239.762H107.27L66.64 199.132C64.55 199.852 62.32 200.262 60 200.262C48.7 200.262 39.5 191.062 39.5 179.762C39.5 168.462 48.7 159.262 60 159.262C71.3 159.262 80.5 168.462 80.5 179.762C80.5 182.092 80.09 184.322 79.37 186.412L111 218.042V102.162C104.2 98.8225 99.5 91.8425 99.5 83.7725C99.5 72.4725 108.7 63.2725 120 63.2725C131.3 63.2725 140.5 72.4725 140.5 83.7725C140.5 91.8425 135.8 98.8225 129 102.162V183.432L160.46 151.972C159.84 150.012 159.5 147.932 159.5 145.772C159.5 134.472 168.7 125.272 180 125.272C191.3 125.272 200.5 134.472 200.5 145.772C200.5 157.072 191.3 166.272 180 166.272C177.5 166.272 175.12 165.802 172.91 164.982L129 208.892V239.772H225C233.25 239.772 240 233.022 240 224.772V134.772C240 126.522 235.23 115.002 229.39 109.162V109.153Z"
fill="#18BCF2"
/>
</svg>
<div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer"></div>
</div>

View File

@@ -43,8 +43,8 @@ const generateMeanStatistics = (
period === "day"
? addDays(currentDate, 1)
: period === "month"
? addMonths(currentDate, 1)
: addHours(currentDate, 1);
? addMonths(currentDate, 1)
: addHours(currentDate, 1);
}
return statistics;
};
@@ -80,8 +80,8 @@ const generateSumStatistics = (
period === "day"
? addDays(currentDate, 1)
: period === "month"
? addMonths(currentDate, 1)
: addHours(currentDate, 1);
? addMonths(currentDate, 1)
: addHours(currentDate, 1);
}
return statistics;
};

View File

@@ -11,7 +11,7 @@ class DemoBlackWhiteRow extends LitElement {
@property() value!: any;
@property({ type: Boolean }) public disabled = false;
@property() disabled = false;
protected render(): TemplateResult {
return html`

View File

@@ -11,11 +11,11 @@ export interface DemoCardConfig {
@customElement("demo-card")
class DemoCard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public hass!: HomeAssistant;
@property({ attribute: false }) public config!: DemoCardConfig;
@property() public config!: DemoCardConfig;
@property({ type: Boolean }) public showConfig = false;
@property() public showConfig = false;
@state() private _size?: number;

View File

@@ -10,9 +10,9 @@ import "../ha-demo-options";
@customElement("demo-cards")
class DemoCards extends LitElement {
@property({ attribute: false }) public configs!: DemoCardConfig[];
@property() public configs!: DemoCardConfig[];
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public hass!: HomeAssistant;
@state() private _showConfig = false;

View File

@@ -8,11 +8,11 @@ import { HomeAssistant } from "../../../src/types";
@customElement("demo-more-info")
class DemoMoreInfo extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public hass!: HomeAssistant;
@property() public entityId!: string;
@property({ type: Boolean }) public showConfig = false;
@property() public showConfig!: boolean;
render() {
const state = this._getState(this.entityId, this.hass.states);
@@ -23,7 +23,7 @@ class DemoMoreInfo extends LitElement {
<state-card-content
.stateObj=${state}
.hass=${this.hass}
inDialog
in-dialog
></state-card-content>
<more-info-content

View File

@@ -1,19 +1,19 @@
import { LitElement, css, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, property } from "lit/decorators";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
import "../../../src/components/ha-formfield";
import "../../../src/components/ha-switch";
import { HomeAssistant } from "../../../src/types";
import "../ha-demo-options";
import "./demo-more-info";
import "../ha-demo-options";
import { HomeAssistant } from "../../../src/types";
@customElement("demo-more-infos")
class DemoMoreInfos extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public hass!: HomeAssistant;
@property({ type: Array }) public entities!: string[];
@property() public entities!: [];
@state() private _showConfig = false;
@property({ attribute: false }) _showConfig: boolean = false;
render() {
return html`

View File

@@ -509,7 +509,7 @@ export default {
away_mode: "on",
aux_heat: "off",
unit_of_measurement: "°C",
friendly_name: "HVAC",
friendly_name: "Hvac",
supported_features: 3833,
},
last_changed: "2018-07-19T10:44:46.200650+00:00",

View File

@@ -1,14 +1,14 @@
import { mdiMenu } from "@mdi/js";
import "@material/mwc-drawer";
import "@material/mwc-top-app-bar-fixed";
import { mdiMenu } from "@mdi/js";
import { LitElement, PropertyValues, css, html } from "lit";
import { customElement, query, state } from "lit/decorators";
import { dynamicElement } from "../../src/common/dom/dynamic-element-directive";
import { HaExpansionPanel } from "../../src/components/ha-expansion-panel";
import { html, css, LitElement, PropertyValues } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../src/components/ha-icon-button";
import "../../src/managers/notification-manager";
import { HaExpansionPanel } from "../../src/components/ha-expansion-panel";
import { haStyle } from "../../src/resources/styles";
import { PAGES, SIDEBAR } from "../build/import-pages";
import { dynamicElement } from "../../src/common/dom/dynamic-element-directive";
import "./components/page-description";
const GITHUB_DEMO_URL =
@@ -24,7 +24,7 @@ const FAKE_HASS = {
@customElement("ha-gallery")
class HaGallery extends LitElement {
@state() private _page =
@property() private _page =
document.location.hash.substring(1) ||
`${SIDEBAR[0].category}/${SIDEBAR[0].pages![0]}`;

View File

@@ -80,7 +80,7 @@ const SCHEMAS: { name: string; conditions: ConditionWithShorthand[] }[] = [
];
@customElement("demo-automation-editor-condition")
export class DemoAutomationEditorCondition extends LitElement {
class DemoHaAutomationEditorCondition extends LitElement {
@state() private hass!: HomeAssistant;
@state() private _disabled = false;
@@ -155,6 +155,6 @@ export class DemoAutomationEditorCondition extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-automation-editor-condition": DemoAutomationEditorCondition;
"demo-ha-automation-editor-condition": DemoHaAutomationEditorCondition;
}
}

View File

@@ -126,7 +126,7 @@ const SCHEMAS: { name: string; triggers: Trigger[] }[] = [
];
@customElement("demo-automation-editor-trigger")
export class DemoAutomationEditorTrigger extends LitElement {
class DemoHaAutomationEditorTrigger extends LitElement {
@state() private hass!: HomeAssistant;
@state() private _disabled = false;
@@ -201,6 +201,6 @@ export class DemoAutomationEditorTrigger extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-automation-editor-trigger": DemoAutomationEditorTrigger;
"demo-ha-automation-editor-trigger": DemoHaAutomationEditorTrigger;
}
}

View File

@@ -1,10 +1,8 @@
import { css, html, LitElement, TemplateResult, nothing } from "lit";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/components/chips/ha-chip-set";
import "../../../../src/components/chips/ha-assist-chip";
import "../../../../src/components/chips/ha-input-chip";
import "../../../../src/components/chips/ha-filter-chip";
import "../../../../src/components/ha-chip";
import "../../../../src/components/ha-chip-set";
import "../../../../src/components/ha-svg-icon";
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
@@ -12,6 +10,10 @@ const chips: {
icon?: string;
content?: string;
}[] = [
{},
{
icon: mdiHomeAssistant,
},
{
content: "Content",
},
@@ -27,73 +29,31 @@ export class DemoHaChips extends LitElement {
return html`
<ha-card header="ha-chip demo">
<div class="card-content">
<p>Action chip</p>
${chips.map(
(chip) => html`
<ha-chip .hasIcon=${chip.icon !== undefined}>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: ""}
${chip.content}
</ha-chip>
`
)}
</div>
</ha-card>
<ha-card header="ha-chip-set demo">
<div class="card-content">
<ha-chip-set>
${chips.map(
(chip) => html`
<ha-assist-chip .label=${chip.content}>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-assist-chip>
`
)}
${chips.map(
(chip) => html`
<ha-assist-chip .label=${chip.content} selected>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-assist-chip>
`
)}
</ha-chip-set>
<p>Filter chip</p>
<ha-chip-set>
${chips.map(
(chip) => html`
<ha-filter-chip .label=${chip.content}>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-filter-chip>
`
)}
${chips.map(
(chip) => html`
<ha-filter-chip .label=${chip.content} selected>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-filter-chip>
`
)}
</ha-chip-set>
<p>Input chip</p>
<ha-chip-set>
${chips.map(
(chip) => html`
<ha-input-chip .label=${chip.content}>
<ha-chip .hasIcon=${chip.icon !== undefined}>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: ""}
${chip.content}
</ha-input-chip>
`
)}
${chips.map(
(chip) => html`
<ha-input-chip .label=${chip.content} selected>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-input-chip>
</ha-chip>
`
)}
</ha-chip-set>
@@ -108,10 +68,12 @@ export class DemoHaChips extends LitElement {
max-width: 600px;
margin: 24px auto;
}
ha-chip {
margin-bottom: 4px;
}
.card-content {
display: flex;
flex-direction: column;
align-items: flex-start;
}
`;
}

View File

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

View File

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

View File

@@ -59,7 +59,7 @@ export class DemoHaBarButton extends LitElement {
<ha-control-button
class=${ifDefined(btn.class)}
label=${ifDefined(btn.label)}
?disabled=${btn.disabled}
disabled=${ifDefined(btn.disabled)}
>
<ha-svg-icon .path=${btn.icon || mdiLightbulb}></ha-svg-icon>
</ha-control-button>

View File

@@ -11,7 +11,6 @@ const buttons: {
min?: number;
max?: number;
step?: number;
unit?: string;
class?: string;
}[] = [
{
@@ -30,11 +29,6 @@ const buttons: {
label: "Custom",
class: "custom",
},
{
id: "unit",
label: "With unit",
unit: "m",
},
];
@customElement("demo-components-ha-control-number-buttons")
@@ -56,7 +50,6 @@ export class DemoHarControlNumberButtons extends LitElement {
<pre>Config: ${JSON.stringify(config)}</pre>
<ha-control-number-buttons
.value=${this.value}
.unit=${config.unit}
.min=${config.min}
.max=${config.max}
.step=${config.step}

View File

@@ -135,7 +135,7 @@ export class DemoHaControlSelect extends LitElement {
class=${ifDefined(config.class)}
@value-changed=${this.handleValueChanged}
aria-labelledby=${id}
?disabled=${config.disabled}
disabled=${ifDefined(config.disabled)}
>
</ha-control-select>
</div>
@@ -156,7 +156,7 @@ export class DemoHaControlSelect extends LitElement {
class=${ifDefined(config.class)}
@value-changed=${this.handleValueChanged}
aria-labelledby=${id}
?disabled=${config.disabled}
disabled=${ifDefined(config.disabled)}
>
</ha-control-select>
`;

View File

@@ -9,7 +9,6 @@ const sliders: {
id: string;
label: string;
mode?: "start" | "end" | "cursor";
unit?: string;
class?: string;
}[] = [
{
@@ -32,21 +31,18 @@ const sliders: {
label: "Slider (start mode) and custom style",
mode: "start",
class: "custom",
unit: "mm",
},
{
id: "slider-end-custom",
label: "Slider (end mode) and custom style",
mode: "end",
class: "custom",
unit: "mm",
},
{
id: "slider-cursor-custom",
label: "Slider (cursor mode) and custom style",
mode: "cursor",
class: "custom",
unit: "mm",
},
];
@@ -97,7 +93,6 @@ export class DemoHaBarSlider extends LitElement {
@value-changed=${this.handleValueChanged}
@slider-moved=${this.handleSliderMoved}
aria-labelledby=${id}
.unit=${config.unit}
>
</ha-control-slider>
</div>
@@ -119,7 +114,6 @@ export class DemoHaBarSlider extends LitElement {
@value-changed=${this.handleValueChanged}
@slider-moved=${this.handleSliderMoved}
aria-label=${label}
.unit=${config.unit}
>
</ha-control-slider>
`;

View File

@@ -63,8 +63,8 @@ export class DemoHaControlSwitch extends LitElement {
.pathOn=${mdiLightbulb}
.pathOff=${mdiLightbulbOff}
aria-labelledby=${id}
?disabled=${config.disabled}
?reversed=${config.reversed}
disabled=${ifDefined(config.disabled)}
reversed=${ifDefined(config.reversed)}
>
</ha-control-switch>
</div>
@@ -86,8 +86,8 @@ export class DemoHaControlSwitch extends LitElement {
aria-label=${label}
.pathOn=${mdiGarageOpen}
.pathOff=${mdiGarage}
?disabled=${config.disabled}
?reversed=${config.reversed}
disabled=${ifDefined(config.disabled)}
reversed=${ifDefined(config.reversed)}
>
</ha-control-switch>
`;

View File

@@ -5,22 +5,9 @@ subtitle: Dialogs provide important prompts in a user flow.
# Material Design 3
Our dialogs are based on the latest version of Material Design. Please note that we have made some well-considered adjustments to these guideliness. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
Our dialogs are based on the latest version of Material Design. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
# Guidelines
## Design
- Dialogs have a max width of 560px. Alert and confirmation dialogs got a fixed width of 320px. If you need more width, consider a dedicated page instead.
- The close X-icon is on the top left, on all screen sizes. Except for alert and confirmation dialogs, they only have buttons and no X-icon. This is different compared to the Material guideliness.
- Dialogs can't be closed with ESC or clicked outside of the dialog when there is a form that the user needs to fill out. Instead it will animate "no" by a little shake.
- Extra icon buttons are on the top right, for example help, settings and expand dialog. More than 2 icon buttons, they will be in an overflow menu.
- The submit button is grouped with a cancel button at the bottom right, on all screen sizes. Fullscreen mobile dialogs have them sticky at the bottom.
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
- Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
- Destructive actions should be a red warning button.
- Alert or confirmation dialogs only have buttons and no X-icon.
- Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
# Highlighted guidelines
## Content
@@ -30,6 +17,14 @@ Our dialogs are based on the latest version of Material Design. Please note that
- If users become unsure, they read the description. Make sure this explains what will happen.
- Strive for minimalism.
## Buttons and X-icon
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
- Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
- Destructive actions should be a red warning button.
- Alert or confirmation dialogs only have buttons and no X-icon.
- Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
## Example
### Confirmation dialog

View File

@@ -3,7 +3,6 @@ import { css, html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-expansion-panel";
import "../../../../src/components/ha-icon-button";
import "../../../../src/components/ha-markdown";
import "../../components/demo-black-white-row";
import { LONG_TEXT } from "../../data/text";

View File

@@ -10,7 +10,6 @@ import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervis
import { computeInitialHaFormData } from "../../../../src/components/ha-form/compute-initial-ha-form-data";
import "../../../../src/components/ha-form/ha-form";
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
import type { AreaRegistryEntry } from "../../../../src/data/area_registry";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../../src/types";
@@ -98,25 +97,22 @@ const DEVICES = [
},
];
const AREAS: AreaRegistryEntry[] = [
const AREAS = [
{
area_id: "backyard",
name: "Backyard",
icon: null,
picture: null,
aliases: [],
},
{
area_id: "bedroom",
name: "Bedroom",
icon: "mdi:bed",
picture: null,
aliases: [],
},
{
area_id: "livingroom",
name: "Livingroom",
icon: "mdi:sofa",
picture: null,
aliases: [],
},

View File

@@ -9,7 +9,6 @@ import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
import "../../../../src/components/ha-selector/ha-selector";
import "../../../../src/components/ha-settings-row";
import type { AreaRegistryEntry } from "../../../../src/data/area_registry";
import { BlueprintInput } from "../../../../src/data/blueprint";
import { showDialog } from "../../../../src/dialogs/make-dialog-manager";
import { getEntity } from "../../../../src/fake_data/entity";
@@ -94,25 +93,22 @@ const DEVICES = [
},
];
const AREAS: AreaRegistryEntry[] = [
const AREAS = [
{
area_id: "backyard",
name: "Backyard",
icon: null,
picture: null,
aliases: [],
},
{
area_id: "bedroom",
name: "Bedroom",
icon: "mdi:bed",
picture: null,
aliases: [],
},
{
area_id: "livingroom",
name: "Livingroom",
icon: "mdi:sofa",
picture: null,
aliases: [],
},

View File

@@ -18,7 +18,7 @@ The Home Assistant interface is based on Material Design. It's a design system c
We want to make it as easy for designers to contribute as it is for developers. Theres a lot a designer can contribute to:
- Meet us at <a href="https://www.home-assistant.io/join-chat" rel="noopener noreferrer" target="_blank">devs_ux Discord</a>. Feel free to share your designs, user test or strategic ideas.
- Meet us at <a href="https://discord.gg/BPBc8rZ9" rel="noopener noreferrer" target="_blank">devs_ux Discord</a>. Feel free to share your designs, user test or strategic ideas.
- Start designing with our <a href="https://www.figma.com/community/file/967153512097289521/Home-Assistant-DesignKit" rel="noopener noreferrer" target="_blank">Figma DesignKit</a>.
- Find the latest UX <a href="https://github.com/home-assistant/frontend/discussions?discussions_q=label%3Aux" rel="noopener noreferrer" target="_blank">discussions</a> and <a href="https://github.com/home-assistant/frontend/labels/ux" rel="noopener noreferrer" target="_blank">issues</a> on GitHub. Everyone can start a new issue or discussion!

View File

@@ -1,20 +1,19 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { html, css, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
import { formatDateTimeNumeric } from "../../../../src/common/datetime/format_date_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { formatDateTimeNumeric } from "../../../../src/common/datetime/format_date_time";
import { timeOptions } from "../../data/date-options";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import {
DateFormat,
FirstWeekday,
FrontendLocaleData,
NumberFormat,
TimeFormat,
DateFormat,
FirstWeekday,
TimeZone,
} from "../../../../src/data/translation";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { timeOptions } from "../../data/date-options";
@customElement("demo-date-time-date-time-numeric")
export class DemoDateTimeDateTimeNumeric extends LitElement {

View File

@@ -1,20 +1,19 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { html, css, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
import { formatDateTimeWithSeconds } from "../../../../src/common/datetime/format_date_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { formatDateTimeWithSeconds } from "../../../../src/common/datetime/format_date_time";
import { timeOptions } from "../../data/date-options";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import {
DateFormat,
FirstWeekday,
FrontendLocaleData,
NumberFormat,
TimeFormat,
DateFormat,
FirstWeekday,
TimeZone,
} from "../../../../src/data/translation";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { timeOptions } from "../../data/date-options";
@customElement("demo-date-time-date-time-seconds")
export class DemoDateTimeDateTimeSeconds extends LitElement {

View File

@@ -1,20 +1,19 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { html, css, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
import { formatShortDateTimeWithYear } from "../../../../src/common/datetime/format_date_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { formatShortDateTimeWithYear } from "../../../../src/common/datetime/format_date_time";
import { timeOptions } from "../../data/date-options";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import {
DateFormat,
FirstWeekday,
FrontendLocaleData,
NumberFormat,
TimeFormat,
DateFormat,
FirstWeekday,
TimeZone,
} from "../../../../src/data/translation";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { timeOptions } from "../../data/date-options";
@customElement("demo-date-time-date-time-short-year")
export class DemoDateTimeDateTimeShortYear extends LitElement {

View File

@@ -1,20 +1,19 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { html, css, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
import { formatShortDateTime } from "../../../../src/common/datetime/format_date_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { formatShortDateTime } from "../../../../src/common/datetime/format_date_time";
import { timeOptions } from "../../data/date-options";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import {
DateFormat,
FirstWeekday,
FrontendLocaleData,
NumberFormat,
TimeFormat,
DateFormat,
FirstWeekday,
TimeZone,
} from "../../../../src/data/translation";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { timeOptions } from "../../data/date-options";
@customElement("demo-date-time-date-time-short")
export class DemoDateTimeDateTimeShort extends LitElement {

View File

@@ -1,20 +1,19 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { html, css, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
import { formatDateTime } from "../../../../src/common/datetime/format_date_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { formatDateTime } from "../../../../src/common/datetime/format_date_time";
import { timeOptions } from "../../data/date-options";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import {
DateFormat,
FirstWeekday,
FrontendLocaleData,
NumberFormat,
TimeFormat,
DateFormat,
FirstWeekday,
TimeZone,
} from "../../../../src/data/translation";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { timeOptions } from "../../data/date-options";
@customElement("demo-date-time-date-time")
export class DemoDateTimeDateTime extends LitElement {

View File

@@ -1,20 +1,18 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { html, css, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
import { formatTimeWithSeconds } from "../../../../src/common/datetime/format_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { formatTimeWithSeconds } from "../../../../src/common/datetime/format_time";
import { timeOptions } from "../../data/date-options";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import {
DateFormat,
FirstWeekday,
FrontendLocaleData,
NumberFormat,
TimeFormat,
DateFormat,
FirstWeekday,
TimeZone,
} from "../../../../src/data/translation";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { timeOptions } from "../../data/date-options";
@customElement("demo-date-time-time-seconds")
export class DemoDateTimeTimeSeconds extends LitElement {

View File

@@ -1,20 +1,18 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { html, css, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
import { formatTimeWeekday } from "../../../../src/common/datetime/format_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { formatTimeWeekday } from "../../../../src/common/datetime/format_time";
import { timeOptions } from "../../data/date-options";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import {
DateFormat,
FirstWeekday,
FrontendLocaleData,
NumberFormat,
TimeFormat,
DateFormat,
FirstWeekday,
TimeZone,
} from "../../../../src/data/translation";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { timeOptions } from "../../data/date-options";
@customElement("demo-date-time-time-weekday")
export class DemoDateTimeTimeWeekday extends LitElement {

View File

@@ -1,20 +1,19 @@
import "@material/mwc-list/mwc-list";
import { LitElement, css, html } from "lit";
import { html, css, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
import { formatTime } from "../../../../src/common/datetime/format_time";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-control-select";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { formatTime } from "../../../../src/common/datetime/format_time";
import { timeOptions } from "../../data/date-options";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import {
DateFormat,
FirstWeekday,
FrontendLocaleData,
NumberFormat,
TimeFormat,
DateFormat,
FirstWeekday,
TimeZone,
} from "../../../../src/data/translation";
import { demoConfig } from "../../../../src/fake_data/demo_config";
import { translationMetadata } from "../../../../src/resources/translations-metadata";
import { timeOptions } from "../../data/date-options";
@customElement("demo-date-time-time")
export class DemoDateTimeTime extends LitElement {

View File

@@ -65,23 +65,15 @@ const CONFIGS = [
>> ...by using additional greater-than signs right next to each other...
> > > ...or with spaces between arrows.
> [!NOTE]
> This is a GitHub note alert
> **Warning** Hey there
> This is a warning with a title
> [!TIP]
> This is a GitHub tip alert
> **Note**
> This is a note
> [!IMPORTANT]
> This is a GitHub important alert
> [!WARNING]
> This is a GitHub warning alert
> [!CAUTION]
> This is a GitHub caution alert
> [!TIP]
> - This is a list entry in GitHub tip alert
> **Note**
> This is a multiline note
> Lorem ipsum...
## Lists

View File

@@ -55,7 +55,7 @@ const CONFIGS = [
];
@customElement("demo-lovelace-media-player-row")
export class DemoLovelaceMediaPlayerRow extends LitElement {
class DemoHuiMediaPlayerRow extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
@@ -73,6 +73,6 @@ export class DemoLovelaceMediaPlayerRow extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-lovelace-media-player-row": DemoLovelaceMediaPlayerRow;
"demo-lovelace-media-player-rows": DemoHuiMediaPlayerRow;
}
}

View File

@@ -35,18 +35,6 @@ const ENTITIES = [
friendly_name: "Nest",
supported_features: 43,
}),
getEntity("climate", "sensibo", "fan_only", {
current_temperature: null,
temperature: null,
min_temp: 0,
max_temp: 1,
target_temp_step: 1,
hvac_modes: ["fan_only", "off"],
friendly_name: "Sensibo purifier",
fan_modes: ["low", "high"],
fan_mode: "low",
supported_features: 9,
}),
getEntity("climate", "unavailable", "unavailable", {
supported_features: 43,
}),
@@ -69,23 +57,6 @@ const CONFIGS = [
entity: climate.nest
`,
},
{
heading: "Fan only example",
config: `
- type: thermostat
entity: climate.sensibo
features:
- type: climate-hvac-modes
hvac_modes:
- fan_only
- 'off'
- type: climate-fan-modes
style: icons
fan_modes:
- low
- high
`,
},
{
heading: "Unavailable",
config: `

View File

@@ -79,18 +79,6 @@ const CONFIGS = [
color: pink
`,
},
{
heading: "Whole tile tap action",
config: `
- type: tile
entity: switch.tv_outlet
color: pink
tap_action:
action: toggle
icon_tap_action:
action: none
`,
},
{
heading: "Unknown entity",
config: `

View File

@@ -10,6 +10,7 @@ import { computeStateDisplay } from "../../../../src/common/entity/compute_state
import "../../../../src/components/data-table/ha-data-table";
import type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table";
import "../../../../src/components/entity/state-badge";
import "../../../../src/components/ha-chip";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../../src/types";
@@ -53,7 +54,6 @@ const SENSOR_DEVICE_CLASSES = [
"volatile_organic_compounds_parts",
"voltage",
"volume",
"volume_flow_rate",
"water",
"weight",
"wind_speed",
@@ -345,7 +345,6 @@ export class DemoEntityState extends LitElement {
title: "Icon",
template: (entry) => html`
<state-badge
.hass=${hass}
.stateObj=${entry.stateObj}
.stateColor=${true}
></state-badge>

View File

@@ -2,7 +2,7 @@ import "@material/mwc-button";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../../src/components/ha-card";
import { ActionHandlerEvent } from "../../../../src/data/lovelace/action_handler";
import { ActionHandlerEvent } from "../../../../src/data/lovelace";
import { actionHandler } from "../../../../src/panels/lovelace/common/directives/action-handler-directive";
@customElement("demo-misc-util-long-press")
@@ -59,9 +59,3 @@ export class DemoUtilLongPress extends LitElement {
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"demo-misc-util-long-press": DemoUtilLongPress;
}
}

View File

@@ -31,21 +31,6 @@ const ENTITIES = [
max_temp: 30,
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
}),
getEntity("climate", "fan", "fan_only", {
friendly_name: "Basic fan",
hvac_modes: ["fan_only", "off"],
hvac_mode: "fan_only",
fan_modes: ["low", "high"],
fan_mode: "low",
current_temperature: null,
temperature: null,
min_temp: 0,
max_temp: 1,
target_temp_step: 1,
supported_features:
// eslint-disable-next-line no-bitwise
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE,
}),
getEntity("climate", "hvac", "auto", {
friendly_name: "Basic hvac",
hvac_modes: ["auto", "off"],
@@ -92,7 +77,7 @@ const ENTITIES = [
@customElement("demo-more-info-climate")
class DemoMoreInfoClimate extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;

View File

@@ -141,7 +141,7 @@ const ENTITIES = [
@customElement("demo-more-info-cover")
class DemoMoreInfoCover extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;

View File

@@ -29,7 +29,7 @@ const ENTITIES = [
@customElement("demo-more-info-humidifier")
class DemoMoreInfoHumidifier extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;

View File

@@ -32,7 +32,7 @@ const ENTITIES = [
@customElement("demo-more-info-input-number")
class DemoMoreInfoInputNumber extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;

View File

@@ -1,3 +0,0 @@
---
title: Input Text
---

View File

@@ -1,46 +0,0 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import {
MockHomeAssistant,
provideHass,
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
const ENTITIES = [
getEntity("input_text", "text", "Inspiration", {
friendly_name: "Text",
mode: "text",
}),
];
@customElement("demo-more-info-input-text")
class DemoMoreInfoInputText extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-input-text": DemoMoreInfoInputText;
}
}

View File

@@ -140,7 +140,7 @@ const ENTITIES = [
@customElement("demo-more-info-light")
class DemoMoreInfoLight extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;

View File

@@ -1,3 +0,0 @@
---
title: Lock
---

View File

@@ -1,49 +0,0 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import {
MockHomeAssistant,
provideHass,
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
const ENTITIES = [
getEntity("lock", "lock", "locked", {
friendly_name: "Lock",
device_class: "lock",
}),
getEntity("lock", "unavailable", "unavailable", {
friendly_name: "Unavailable lock",
}),
];
@customElement("demo-more-info-lock")
class DemoMoreInfoLock extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-lock": DemoMoreInfoLock;
}
}

View File

@@ -1,3 +0,0 @@
---
title: Media Player
---

View File

@@ -1,41 +0,0 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import {
MockHomeAssistant,
provideHass,
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
import { createMediaPlayerEntities } from "../../data/media_players";
const ENTITIES = createMediaPlayerEntities();
@customElement("demo-more-info-media-player")
class DemoMoreInfoMediaPlayer extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-media-player": DemoMoreInfoMediaPlayer;
}
}

View File

@@ -1,3 +0,0 @@
---
title: Number
---

View File

@@ -1,78 +0,0 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import {
MockHomeAssistant,
provideHass,
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
const ENTITIES = [
getEntity("number", "box1", 0, {
friendly_name: "Box1",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "box",
unit_of_measurement: "items",
}),
getEntity("number", "slider1", 0, {
friendly_name: "Slider1",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "slider",
unit_of_measurement: "items",
}),
getEntity("number", "auto1", 0, {
friendly_name: "Auto1",
min: 0,
max: 1000,
step: 1,
initial: 0,
mode: "auto",
unit_of_measurement: "items",
}),
getEntity("number", "auto2", 0, {
friendly_name: "Auto2",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "auto",
unit_of_measurement: "items",
}),
];
@customElement("demo-more-info-number")
class DemoMoreInfoNumber extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-number": DemoMoreInfoNumber;
}
}

View File

@@ -1,3 +0,0 @@
---
title: Scene
---

View File

@@ -1,49 +0,0 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import {
MockHomeAssistant,
provideHass,
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
const ENTITIES = [
getEntity("scene", "romantic_lights", "scening", {
entity_id: ["light.bed_light", "light.ceiling_lights"],
friendly_name: "Romantic Scene",
}),
getEntity("scene", "unavailable", "unavailable", {
friendly_name: "Romantic Scene",
}),
];
@customElement("demo-more-info-scene")
class DemoMoreInfoScene extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-scene": DemoMoreInfoScene;
}
}

View File

@@ -1,3 +0,0 @@
---
title: Timer
---

View File

@@ -1,46 +0,0 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import {
MockHomeAssistant,
provideHass,
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
const ENTITIES = [
getEntity("timer", "timer", "idle", {
friendly_name: "Timer",
duration: "0:05:00",
}),
];
@customElement("demo-more-info-timer")
class DemoMoreInfoTimer extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-timer": DemoMoreInfoTimer;
}
}

View File

@@ -1,6 +1,12 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import {
UPDATE_SUPPORT_BACKUP,
UPDATE_SUPPORT_PROGRESS,
UPDATE_SUPPORT_INSTALL,
UPDATE_SUPPORT_RELEASE_NOTES,
} from "../../../../src/data/update";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import {
@@ -9,14 +15,13 @@ import {
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
import { LONG_TEXT } from "../../data/text";
import { UpdateEntityFeature } from "../../../../src/data/update";
const base_attributes = {
title: "Awesome",
installed_version: "1.2.2",
latest_version: "1.2.3",
release_url: "https://home-assistant.io",
supported_features: UpdateEntityFeature.INSTALL,
supported_features: UPDATE_SUPPORT_INSTALL,
skipped_version: null,
in_progress: false,
release_summary:
@@ -56,7 +61,7 @@ const ENTITIES = [
getEntity("update", "update7", "on", {
...base_attributes,
supported_features:
base_attributes.supported_features + UpdateEntityFeature.BACKUP,
base_attributes.supported_features + UPDATE_SUPPORT_BACKUP,
friendly_name: "With backup support",
}),
getEntity("update", "update8", "on", {
@@ -68,21 +73,21 @@ const ENTITIES = [
...base_attributes,
in_progress: 25,
supported_features:
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
base_attributes.supported_features + UPDATE_SUPPORT_PROGRESS,
friendly_name: "With 25 in_progress",
}),
getEntity("update", "update10", "on", {
...base_attributes,
in_progress: 50,
supported_features:
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
base_attributes.supported_features + UPDATE_SUPPORT_PROGRESS,
friendly_name: "With 50 in_progress",
}),
getEntity("update", "update11", "on", {
...base_attributes,
in_progress: 75,
supported_features:
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
base_attributes.supported_features + UPDATE_SUPPORT_PROGRESS,
friendly_name: "With 75 in_progress",
}),
getEntity("update", "update12", "unavailable", {
@@ -109,19 +114,19 @@ const ENTITIES = [
...base_attributes,
friendly_name: "Update with release notes",
supported_features:
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
base_attributes.supported_features + UPDATE_SUPPORT_RELEASE_NOTES,
}),
getEntity("update", "update17", "off", {
...base_attributes,
friendly_name: "Update with release notes error",
supported_features:
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
base_attributes.supported_features + UPDATE_SUPPORT_RELEASE_NOTES,
}),
getEntity("update", "update18", "off", {
...base_attributes,
friendly_name: "Update with release notes loading",
supported_features:
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
base_attributes.supported_features + UPDATE_SUPPORT_RELEASE_NOTES,
}),
getEntity("update", "update19", "on", {
...base_attributes,
@@ -137,16 +142,15 @@ const ENTITIES = [
getEntity("update", "update21", "on", {
...base_attributes,
in_progress: true,
friendly_name:
"Update with in_progress true and UpdateEntityFeature.PROGRESS",
friendly_name: "Update with in_progress true and UPDATE_SUPPORT_PROGRESS",
supported_features:
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
base_attributes.supported_features + UPDATE_SUPPORT_PROGRESS,
}),
];
@customElement("demo-more-info-update")
class DemoMoreInfoUpdate extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;

View File

@@ -1,3 +0,0 @@
---
title: Vacuum
---

View File

@@ -1,50 +0,0 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import {
MockHomeAssistant,
provideHass,
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
import { VacuumEntityFeature } from "../../../../src/data/vacuum";
const ENTITIES = [
getEntity("vacuum", "first_floor_vacuum", "docked", {
friendly_name: "First floor vacuum",
supported_features:
VacuumEntityFeature.START +
VacuumEntityFeature.STOP +
VacuumEntityFeature.RETURN_HOME,
}),
];
@customElement("demo-more-info-vacuum")
class DemoMoreInfoVacuum extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-vacuum": DemoMoreInfoVacuum;
}
}

View File

@@ -42,7 +42,7 @@ const ENTITIES = [
@customElement("demo-more-info-water-heater")
class DemoMoreInfoWaterHeater extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;

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