Compare commits

..

1 Commits

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -26,7 +26,7 @@ jobs:
ref: dev ref: dev
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.3.0 uses: actions/setup-node@v4.1.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@@ -62,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.1.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn

View File

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

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.1.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@@ -63,7 +63,7 @@ jobs:
ref: master ref: master
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.3.0 uses: actions/setup-node@v4.1.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn

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.1.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn

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.1.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn

View File

@@ -6,7 +6,7 @@ on:
- cron: "0 1 * * *" - cron: "0 1 * * *"
env: env:
PYTHON_VERSION: "3.13" PYTHON_VERSION: "3.12"
NODE_OPTIONS: --max_old_space_size=6144 NODE_OPTIONS: --max_old_space_size=6144
permissions: permissions:
@@ -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.1.0
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@@ -57,14 +57,14 @@ jobs:
run: tar -czvf translations.tar.gz translations run: tar -czvf translations.tar.gz translations
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.2 uses: actions/upload-artifact@v4.4.3
with: with:
name: wheels name: wheels
path: dist/home_assistant_frontend*.whl path: dist/home_assistant_frontend*.whl
if-no-files-found: error if-no-files-found: error
- name: Upload translations - name: Upload translations
uses: actions/upload-artifact@v4.6.2 uses: actions/upload-artifact@v4.4.3
with: with:
name: translations name: translations
path: translations.tar.gz path: translations.tar.gz

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ on:
- published - published
env: env:
PYTHON_VERSION: "3.13" PYTHON_VERSION: "3.12"
NODE_OPTIONS: --max_old_space_size=6144 NODE_OPTIONS: --max_old_space_size=6144
# Set default workflow permissions # Set default workflow permissions
@@ -25,16 +25,16 @@ jobs:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
- name: Verify version
uses: home-assistant/actions/helpers/verify-version@master
- name: Set up Python ${{ env.PYTHON_VERSION }} - name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: ${{ env.PYTHON_VERSION }} python-version: ${{ env.PYTHON_VERSION }}
- name: Verify version
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.1.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.1.0
with: with:
files: | files: |
dist/*.whl dist/*.whl
@@ -74,9 +74,9 @@ 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@2024.11.0
with: with:
abi: cp313 abi: cp312
tag: musllinux_1_2 tag: musllinux_1_2
arch: amd64 arch: amd64
wheels-key: ${{ secrets.WHEELS_KEY }} wheels-key: ${{ secrets.WHEELS_KEY }}
@@ -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.1.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.1.0
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.1.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.1.0
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

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

42
.vscode/tasks.json vendored
View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

@@ -18,7 +18,7 @@ module.exports.sourceMapURL = () => {
module.exports.ignorePackages = () => []; 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 = ({ latestBuild, isHassioBuild }) =>
[ [
// Contains all color definitions for all material color sets. // Contains all color definitions for all material color sets.
// We don't use it // We don't use it
@@ -28,6 +28,12 @@ module.exports.emptyPackages = ({ isHassioBuild }) =>
require.resolve("@polymer/font-roboto/roboto.js"), 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"),
// Compatibility not needed for latest builds
latestBuild &&
// wrapped in require.resolve so it blows up if file no longer exists
require.resolve(
path.resolve(paths.polymer_dir, "src/resources/compatibility.ts")
),
// Icons in supervisor conflict with icons in HA so we don't load. // Icons in supervisor conflict with icons in HA so we don't load.
isHassioBuild && isHassioBuild &&
require.resolve( require.resolve(
@@ -49,7 +55,7 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
__STATIC_PATH__: "/static/", __STATIC_PATH__: "/static/",
__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}" : "${location.protocol}//${location.host}"
}\``, }\``,
"process.env.NODE_ENV": JSON.stringify( "process.env.NODE_ENV": JSON.stringify(

View File

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

View File

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

View File

@@ -56,7 +56,6 @@ const getCommonTemplateVars = () => {
); );
return { return {
modernRegex: compileRegex(browserRegexes.concat(haMacOSRegex)).toString(), modernRegex: compileRegex(browserRegexes.concat(haMacOSRegex)).toString(),
hassUrl: process.env.HASS_URL || "",
}; };
}; };

View File

@@ -59,11 +59,6 @@ function copyPolyfills(staticDir) {
npmPath("@webcomponents/webcomponentsjs/webcomponents-bundle.js.map"), npmPath("@webcomponents/webcomponentsjs/webcomponents-bundle.js.map"),
staticPath("polyfills/") staticPath("polyfills/")
); );
// Lit polyfill support
fs.copySync(
npmPath("lit/polyfill-support.js"),
path.join(staticPath("polyfills/"), "lit-polyfill-support.js")
);
// dialog-polyfill css // dialog-polyfill css
copyFileDir( copyFileDir(
@@ -72,6 +67,12 @@ function copyPolyfills(staticDir) {
); );
} }
function copyLoaderJS(staticDir) {
const staticPath = genStaticPath(staticDir);
copyFileDir(npmPath("systemjs/dist/s.min.js"), staticPath("js"));
copyFileDir(npmPath("systemjs/dist/s.min.js.map"), staticPath("js"));
}
function copyFonts(staticDir) { function copyFonts(staticDir) {
const staticPath = genStaticPath(staticDir); const staticPath = genStaticPath(staticDir);
// Local fonts // Local fonts
@@ -95,10 +96,6 @@ function copyMapPanel(staticDir) {
npmPath("leaflet/dist/leaflet.css"), npmPath("leaflet/dist/leaflet.css"),
staticPath("images/leaflet/") staticPath("images/leaflet/")
); );
copyFileDir(
npmPath("leaflet.markercluster/dist/MarkerCluster.css"),
staticPath("images/leaflet/")
);
fs.copySync( fs.copySync(
npmPath("leaflet/dist/images"), npmPath("leaflet/dist/images"),
staticPath("images/leaflet/images/") staticPath("images/leaflet/images/")
@@ -143,6 +140,8 @@ gulp.task("copy-static-app", async () => {
const staticDir = paths.app_output_static; const staticDir = paths.app_output_static;
// Basic static files // Basic static files
fs.copySync(polyPath("public"), paths.app_output_root); fs.copySync(polyPath("public"), paths.app_output_root);
copyLoaderJS(staticDir);
copyPolyfills(staticDir); copyPolyfills(staticDir);
copyFonts(staticDir); copyFonts(staticDir);
copyTranslations(staticDir); copyTranslations(staticDir);
@@ -165,6 +164,8 @@ gulp.task("copy-static-demo", async () => {
); );
// Copy demo static files // Copy demo static files
fs.copySync(path.resolve(paths.demo_dir, "public"), paths.demo_output_root); fs.copySync(path.resolve(paths.demo_dir, "public"), paths.demo_output_root);
copyLoaderJS(paths.demo_output_static);
copyPolyfills(paths.demo_output_static); copyPolyfills(paths.demo_output_static);
copyMapPanel(paths.demo_output_static); copyMapPanel(paths.demo_output_static);
copyFonts(paths.demo_output_static); copyFonts(paths.demo_output_static);
@@ -178,6 +179,8 @@ gulp.task("copy-static-cast", async () => {
fs.copySync(polyPath("public/static"), paths.cast_output_static); fs.copySync(polyPath("public/static"), paths.cast_output_static);
// Copy cast static files // Copy cast static files
fs.copySync(path.resolve(paths.cast_dir, "public"), paths.cast_output_root); fs.copySync(path.resolve(paths.cast_dir, "public"), paths.cast_output_root);
copyLoaderJS(paths.cast_output_static);
copyPolyfills(paths.cast_output_static); copyPolyfills(paths.cast_output_static);
copyMapPanel(paths.cast_output_static); copyMapPanel(paths.cast_output_static);
copyFonts(paths.cast_output_static); copyFonts(paths.cast_output_static);

View File

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

View File

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

View File

@@ -1,17 +1,12 @@
const { existsSync } = require("fs"); const { existsSync } = require("fs");
const path = require("path"); const path = require("path");
const rspack = require("@rspack/core"); const rspack = require("@rspack/core");
// eslint-disable-next-line @typescript-eslint/naming-convention
const { RsdoctorRspackPlugin } = require("@rsdoctor/rspack-plugin"); const { RsdoctorRspackPlugin } = require("@rsdoctor/rspack-plugin");
// eslint-disable-next-line @typescript-eslint/naming-convention
const { StatsWriterPlugin } = require("webpack-stats-plugin"); const { StatsWriterPlugin } = require("webpack-stats-plugin");
const filterStats = require("@bundle-stats/plugin-webpack-filter"); const filterStats = require("@bundle-stats/plugin-webpack-filter").default;
// eslint-disable-next-line @typescript-eslint/naming-convention
const TerserPlugin = require("terser-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin");
// eslint-disable-next-line @typescript-eslint/naming-convention
const { WebpackManifestPlugin } = require("rspack-manifest-plugin"); const { WebpackManifestPlugin } = require("rspack-manifest-plugin");
const log = require("fancy-log"); const log = require("fancy-log");
// eslint-disable-next-line @typescript-eslint/naming-convention
const WebpackBar = require("webpackbar/rspack"); const WebpackBar = require("webpackbar/rspack");
const paths = require("./paths.cjs"); const paths = require("./paths.cjs");
const bundle = require("./bundle.cjs"); const bundle = require("./bundle.cjs");
@@ -160,7 +155,9 @@ const createRspackConfig = ({
}, },
}), }),
new rspack.NormalModuleReplacementPlugin( new rspack.NormalModuleReplacementPlugin(
new RegExp(bundle.emptyPackages({ isHassioBuild }).join("|")), new RegExp(
bundle.emptyPackages({ latestBuild, isHassioBuild }).join("|")
),
path.resolve(paths.polymer_dir, "src/util/empty.js") path.resolve(paths.polymer_dir, "src/util/empty.js")
), ),
!isProdBuild && new LogStartCompilePlugin(), !isProdBuild && new LogStartCompilePlugin(),
@@ -195,7 +192,6 @@ const createRspackConfig = ({
"lit/directives/if-defined$": "lit/directives/if-defined.js", "lit/directives/if-defined$": "lit/directives/if-defined.js",
"lit/directives/guard$": "lit/directives/guard.js", "lit/directives/guard$": "lit/directives/guard.js",
"lit/directives/cache$": "lit/directives/cache.js", "lit/directives/cache$": "lit/directives/cache.js",
"lit/directives/join$": "lit/directives/join.js",
"lit/directives/repeat$": "lit/directives/repeat.js", "lit/directives/repeat$": "lit/directives/repeat.js",
"lit/directives/live$": "lit/directives/live.js", "lit/directives/live$": "lit/directives/live.js",
"lit/directives/keyed$": "lit/directives/keyed.js", "lit/directives/keyed$": "lit/directives/keyed.js",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -309,7 +309,7 @@ export class HcMain extends HassElement {
"../../../../src/panels/lovelace/strategies/get-strategy" "../../../../src/panels/lovelace/strategies/get-strategy"
); );
const config = await generateLovelaceDashboardStrategy( const config = await generateLovelaceDashboardStrategy(
rawConfig, rawConfig.strategy,
this.hass! this.hass!
); );
this._handleNewLovelaceConfig(config); this._handleNewLovelaceConfig(config);
@@ -351,7 +351,10 @@ export class HcMain extends HassElement {
"../../../../src/panels/lovelace/strategies/get-strategy" "../../../../src/panels/lovelace/strategies/get-strategy"
); );
this._handleNewLovelaceConfig( this._handleNewLovelaceConfig(
await generateLovelaceDashboardStrategy(DEFAULT_CONFIG, this.hass!) await generateLovelaceDashboardStrategy(
DEFAULT_CONFIG.strategy,
this.hass!
)
); );
} }

View File

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

View File

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

View File

@@ -5,7 +5,7 @@ import { until } from "lit/directives/until";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/components/ha-button"; import "../../../src/components/ha-button";
import "../../../src/components/ha-spinner"; import "../../../src/components/ha-circular-progress";
import type { LovelaceCardConfig } from "../../../src/data/lovelace/config/card"; import type { LovelaceCardConfig } from "../../../src/data/lovelace/config/card";
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
import type { import type {
@@ -32,7 +32,6 @@ export class HADemoCard extends LitElement implements LovelaceCard {
return this._hidden ? 0 : 2; return this._hidden ? 0 : 2;
} }
// eslint-disable-next-line @typescript-eslint/no-empty-function
public setConfig(_config: LovelaceCardConfig) {} public setConfig(_config: LovelaceCardConfig) {}
protected render() { protected render() {
@@ -44,7 +43,9 @@ export class HADemoCard extends LitElement implements LovelaceCard {
<div class="picker"> <div class="picker">
<div class="label"> <div class="label">
${this._switching ${this._switching
? html`<ha-spinner></ha-spinner>` ? html`
<ha-circular-progress indeterminate></ha-circular-progress>
`
: until( : until(
selectedDemoConfig.then( selectedDemoConfig.then(
(conf) => html` (conf) => html`

View File

@@ -1,3 +1,5 @@
// Compat needs to be first import
import "../../src/resources/compatibility";
import { customElement } from "lit/decorators"; import { customElement } from "lit/decorators";
import { isNavigationClick } from "../../src/common/dom/is-navigation-click"; import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
import { navigate } from "../../src/common/navigate"; import { navigate } from "../../src/common/navigate";
@@ -63,7 +65,6 @@ export class HaDemo extends HomeAssistantAppEl {
mockEntityRegistry(hass, [ mockEntityRegistry(hass, [
{ {
config_entry_id: "co2signal", config_entry_id: "co2signal",
config_subentry_id: null,
device_id: "co2signal", device_id: "co2signal",
area_id: null, area_id: null,
disabled_by: null, disabled_by: null,
@@ -84,7 +85,6 @@ export class HaDemo extends HomeAssistantAppEl {
}, },
{ {
config_entry_id: "co2signal", config_entry_id: "co2signal",
config_subentry_id: null,
device_id: "co2signal", device_id: "co2signal",
area_id: null, area_id: null,
disabled_by: null, disabled_by: null,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

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

Before

Width:  |  Height:  |  Size: 964 B

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
import { mdiLightbulbOn, mdiPacMan } from "@mdi/js"; import { mdiPacMan } from "@mdi/js";
import type { TemplateResult } from "lit"; import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit"; import { css, html, LitElement } from "lit";
import { customElement } from "lit/decorators"; import { customElement } from "lit/decorators";
@@ -125,23 +125,6 @@ const SAMPLES: {
`; `;
}, },
}, },
{
template(slot, leftChevron) {
return html`
<ha-expansion-panel
slot=${slot}
.leftChevron=${leftChevron}
header="Attr Header with actions"
>
<ha-svg-icon
slot="leading-icon"
.path=${mdiLightbulbOn}
></ha-svg-icon>
${SHORT_TEXT}
</ha-expansion-panel>
`;
},
},
]; ];
@customElement("demo-components-ha-expansion-panel") @customElement("demo-components-ha-expansion-panel")
@@ -160,11 +143,13 @@ export class DemoHaExpansionPanel extends LitElement {
`; `;
} }
static styles = css` static get styles() {
ha-expansion-panel { return css`
margin: -16px; ha-expansion-panel {
} margin: -16px;
`; }
`;
}
} }
declare global { declare global {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
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 } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import "../../../../src/components/ha-spinner"; import "../../../../src/components/ha-circular-progress";
import type { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import type { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import type { Supervisor } from "../../../../src/data/supervisor/supervisor"; import type { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
@@ -21,7 +21,7 @@ class HassioAddonConfigDashboard extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.addon) { if (!this.addon) {
return html`<ha-spinner></ha-spinner>`; return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
} }
const hasConfiguration = const hasConfiguration =
(this.addon.options && Object.keys(this.addon.options).length) || (this.addon.options && Object.keys(this.addon.options).length) ||

View File

@@ -113,9 +113,8 @@ class HassioAddonConfig extends LitElement {
required: entry.required, required: entry.required,
selector: { selector: {
text: { text: {
type: entry.format type:
? entry.format entry.format || MASKED_FIELDS.includes(entry.name)
: MASKED_FIELDS.includes(entry.name)
? "password" ? "password"
: "text", : "text",
}, },
@@ -300,7 +299,7 @@ class HassioAddonConfig extends LitElement {
if (this.addon.schema && this._canShowSchema && !this._yamlMode) { if (this.addon.schema && this._canShowSchema && !this._yamlMode) {
this._valid = true; this._valid = true;
this._configHasChanged = true; this._configHasChanged = true;
this._options = ev.detail.value; this._options! = ev.detail.value;
} else { } else {
this._configHasChanged = true; this._configHasChanged = true;
this._valid = ev.detail.isValid; this._valid = ev.detail.isValid;

View File

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

View File

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

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