Compare commits

...

66 Commits

Author SHA1 Message Date
Wendelin
cea80d9830 Fix more font sizes 2025-04-28 17:44:21 +02:00
Wendelin
fd279ea2b4 Update line-height 2025-04-28 17:33:26 +02:00
Wendelin
335b876fec Update more font sizes 2025-04-28 16:58:11 +02:00
Wendelin
b36b4d734b Update font-weight 2025-04-28 16:55:39 +02:00
Wendelin
514b6568e5 Update rem to use vars 2025-04-28 16:40:09 +02:00
Wendelin
4750a59719 Merge branch 'dev' of github.com:home-assistant/frontend into update-typography 2025-04-28 16:25:33 +02:00
emufan
92521d4565 Themeable badge icon size and badge font size (#25185)
Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com>
2025-04-28 14:20:05 +00:00
Bram Kragten
66dbafb5f5 Render todo items with no state, change look of read only items (#24529)
* Render todo items with no state, change look of read only items

Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com>
Co-authored-by: Wendelin <w@pe8.at>
2025-04-28 16:06:02 +02:00
Yosi Levy
7c46d2d2f4 History tooltip RTL fix (#24917)
* History tooltip RTL fix

* Fix background color
2025-04-28 16:30:26 +03:00
Wendelin
ca642d46cc Add typography styles (#25171)
* Add typoghrapy styles

* Split styles

* Fix duplicated html

* remove unused paper vars

* Fix vars

* Add vars autocompletion extension

* Fix css syntax highlighting
2025-04-28 16:04:47 +03:00
Wendelin
404d6c75b5 Update font-size 2025-04-28 14:58:22 +02:00
renovate[bot]
9a52185e13 Update vaadinWebComponents monorepo to v24.7.4 (#25204)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-28 14:54:24 +03:00
renovate[bot]
39a73774b0 Update rspack monorepo to v1.3.7 (#25206)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-28 14:53:45 +03:00
Wendelin
b25b170539 Update ha cloud backup info translation (#25203)
* Update ha cloud backup info translation

* Update src/translations/en.json
2025-04-28 12:15:18 +02:00
Bram Kragten
6442606fc5 Wait for backup integration when doing a restore (#25188)
Co-authored-by: Wendelin <w@pe8.at>
2025-04-28 07:51:58 +00:00
Wendelin
1b79869c87 Backup retention by location (#25144) 2025-04-28 09:24:34 +02:00
Bram Kragten
672fbc6007 Fix storage decorator timing (#25199) 2025-04-28 08:46:13 +03:00
renovate[bot]
e1899836bf Update dependency marked to v15.0.11 (#25202)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-28 08:28:34 +03:00
Said Tahsin Dane
e90967d200 Scripts - Fix the run button to check if the script has fields (#25156)
* Fix the run button to check if the script has fields

* Used a more straightforward function to check fields instead of an async call
2025-04-27 16:17:55 +00:00
renovate[bot]
631bfe46ba Update dependency @codemirror/view to v6.36.6 (#25194)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 19:06:56 +03:00
Bram Kragten
a5762f07ac Rename entity ids when updating device name during config flow (#25186)
* Rename entity ids when updating device name during config flow

* simplify
2025-04-27 19:06:26 +03:00
renovate[bot]
bf7422e4c5 Update rspack monorepo to v1.3.6 (#25193)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 08:26:19 +02:00
renovate[bot]
45994e7989 Update dependency marked to v15.0.10 (#25191)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 08:21:50 +02:00
Simon Lamon
995e3f10ad Replace remaining mwc-tabs (#25179)
* Remaining mwc-tabs

* Apply suggestions from code review

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

---------

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-04-26 20:12:03 +00:00
karwosts
6464c2b602 Share energy sum calculation across all cards (#25184) 2025-04-26 20:58:33 +03:00
renovate[bot]
8901c1fb31 Update dependency glob to v11.0.2 (#25183)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-26 19:38:54 +03:00
Simon Lamon
1f5f18f7e7 Align add-view button in edit dashboard (#25181)
Align button
2025-04-26 19:29:02 +03:00
Simon Lamon
51ca3e277c Fix wrong label selected when filtering labels (#25180)
Fix filtered labels
2025-04-26 19:27:15 +03:00
J. Nick Koston
ee495a432f Add initial zeroconf panel (#25151)
* Add initial zeroconf panel
2025-04-26 12:14:47 +02:00
Bram Kragten
d75ea3bb8d Replace mwc-tab -> sl-tab (#25166)
* Replace mwc-tab -> sl-tab
2025-04-25 17:05:04 +00:00
Paulus Schoutsen
40fbeaae1c render voice names for Cloud voices (#25164) 2025-04-25 09:43:04 -04:00
renovate[bot]
dc2c6cee21 Update dependency @codemirror/legacy-modes to v6.5.1 (#25177)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 13:24:29 +00:00
Petar Petrov
3b0cd9e3ae Improve handling of batteries in sankey chart (#25175) 2025-04-25 14:18:40 +02:00
ildar170975
834ece8547 Fix height & scroll in hui-dialog-create-card (#25042)
* set min/max height & fix margin for "search" field

* remove restoring height & width

* added scrollTo(top)

* compare _filter with _prevFilter

* use changedProps
2025-04-25 14:24:27 +03:00
Bram Kragten
b7aa296be7 remove wait for person in onboarding user (#25173) 2025-04-25 14:18:53 +03:00
Petar Petrov
0cab6c9e2e Use refactored Z-Wave reconfigure flow (#25174) 2025-04-25 11:18:22 +00:00
Bram Kragten
94c8665528 Improve sidebar styling (#25172) 2025-04-25 14:16:07 +03:00
Simon Lamon
c7ca654926 Clean up polymer paper item CSS variables (#24896)
Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com>
2025-04-25 13:08:54 +02:00
Bram Kragten
488599905b Always use target ES2021 (#25170) 2025-04-25 09:04:05 +00:00
Bram Kragten
221e1d9ed8 Allow to change device names during config flow (#25142)
* Allow to change device names during config flow

* Update zha-device-card.ts

---------

Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2025-04-25 10:04:00 +03:00
Bram Kragten
0229f67751 Replace clickable list item (#25165) 2025-04-25 10:01:19 +03:00
Bram Kragten
3a0c367f76 Add a drag-scroll controller (#25159)
* Add a drag-scroll controller

* simplify and fix
2025-04-25 08:51:35 +03:00
renovate[bot]
af0854e480 Update dependency marked to v15.0.9 (#25168)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 06:18:46 +02:00
Simon Lamon
14f4120926 Fix menu not opening options (#25162) 2025-04-24 20:20:11 +02:00
renovate[bot]
e2bd464001 Update dependency typescript-eslint to v8.31.0 (#25161)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-24 20:09:48 +02:00
renovate[bot]
eb9f81d9a1 Update dependency eslint to v9.25.1 (#25160)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-24 19:32:07 +02:00
Johannes Hager
078209d154 Add Option to wrap or scroll section header badges (#24709) 2025-04-24 15:04:09 +00:00
Wendelin
7a617600ad Add unit tests and docs for common/context (#25158) 2025-04-24 16:57:29 +02:00
Wendelin
ae74e3496c Add typography tokens (#25084) 2025-04-24 16:52:03 +02:00
Petar Petrov
c0f304ad40 Use numeric prefix for IPv6 mask (#25130) 2025-04-24 16:45:37 +02:00
renovate[bot]
c794a2734b Update dependency @lit/context to v1.1.5 (#25157)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-24 12:21:10 +00:00
Bram Kragten
e156dd36f4 Use SWC for typescript, update to Lit 3, migrate decorators (#25150)
Co-authored-by: Wendelin <w@pe8.at>
2025-04-24 14:10:35 +02:00
Wendelin
c40bf8f3cd Add prevent restart when backup is ongoing (#25010)
* Add prevent restart when backup is ongoing

* Fix types

* Add error handling

* Improve review issues
2025-04-24 13:45:42 +03:00
renovate[bot]
93485d8b57 Update vitest monorepo to v3.1.2 (#25155)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-24 13:10:44 +03:00
Paul Bottein
ce5cdaa496 Add badges section on the areas strategy editor (#25154) 2025-04-24 12:36:54 +03:00
Wendelin
dcbaa31c96 Add system-managed addon info (#25132)
* Add system managed addon info dialog

* Add system managed alert

* Fix translation

* Update hassio/src/addon-view/info/hassio-addon-info.ts

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

---------

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2025-04-24 11:12:34 +03:00
Wendelin
71b2e5f827 Improve onboarding styling (#25147) 2025-04-24 10:54:43 +03:00
Wendelin
d58590b534 Add automation loading indicator (#25146) 2025-04-24 08:01:25 +02:00
Wendelin
b2044e88b6 Improve landingpage translation loading (#25148) 2025-04-24 08:33:14 +03:00
Paul Bottein
94b5ed97c6 Refactor multi term fuse search to reuse it (#25143)
* Refactor multi term fuse search to re-use

* Do not create filter when not open

* Update fuse options

* Use fuse options
2025-04-24 08:31:02 +03:00
Zhephyr
11b8f6210f Extend usage of Data Entry Flow forms description_placeholders to field labels (#25115) 2025-04-23 12:46:15 +03:00
Paul Bottein
8e778cfc32 Use new entity naming in statistic picker (#25137) 2025-04-23 08:37:03 +03:00
karwosts
4c6a5ed2e3 convert-color: color names should be case insensitive (#25140)
convert-color: colornames should be case insensitive
2025-04-23 08:14:53 +03:00
Simon Lamon
48c90267df Fix stuck onboarding when no devices are discovered (#25106)
* Broken onboarding

* Introduce a boolean var and return previous code

* Remove import
2025-04-23 08:14:03 +03:00
Simon Lamon
fcab356639 Change netlify actions (#24815)
* Netlify fix

* Fix missing \

* Prettier

* PR review

* npx

* npx
2025-04-22 13:22:03 +02:00
Paul Bottein
a70a0d4b4a Fix event propagation in generic entity row (#25134) 2025-04-22 13:15:20 +02:00
566 changed files with 7072 additions and 4811 deletions

View File

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

View File

@@ -41,9 +41,8 @@ jobs:
- name: Deploy to Netlify
id: deploy
uses: netlify/actions/cli@master
with:
args: deploy --dir=cast/dist --alias dev
run: |
npx -y netlify-cli deploy --dir=cast/dist --alias dev
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_CAST_SITE_ID }}
@@ -77,9 +76,8 @@ jobs:
- name: Deploy to Netlify
id: deploy
uses: netlify/actions/cli@master
with:
args: deploy --dir=cast/dist --prod
run: |
npx -y netlify-cli deploy --dir=cast/dist --prod
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_CAST_SITE_ID }}

View File

@@ -42,9 +42,8 @@ jobs:
- name: Deploy to Netlify
id: deploy
uses: netlify/actions/cli@master
with:
args: deploy --dir=demo/dist --prod
run: |
npx -y netlify-cli deploy --dir=demo/dist --prod
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_DEV_SITE_ID }}
@@ -78,9 +77,8 @@ jobs:
- name: Deploy to Netlify
id: deploy
uses: netlify/actions/cli@master
with:
args: deploy --dir=demo/dist --prod
run: |
npx -y netlify-cli deploy --dir=demo/dist --prod
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_SITE_ID }}

View File

@@ -34,9 +34,8 @@ jobs:
- name: Deploy to Netlify
id: deploy
uses: netlify/actions/cli@master
with:
args: deploy --dir=gallery/dist --prod
run: |
npx -y netlify-cli deploy --dir=gallery/dist --prod
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_GALLERY_SITE_ID }}

View File

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

View File

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

View File

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

View File

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

View File

@@ -73,6 +73,19 @@ module.exports.terserOptions = ({ latestBuild, isTestBuild }) => ({
sourceMap: !isTestBuild,
});
/** @type {import('@rspack/core').SwcLoaderOptions} */
module.exports.swcOptions = () => ({
jsc: {
loose: true,
externalHelpers: true,
target: "ES2021",
parser: {
syntax: "typescript",
decorators: true,
},
},
});
module.exports.babelOptions = ({
latestBuild,
isProdBuild,
@@ -97,7 +110,6 @@ module.exports.babelOptions = ({
shippedProposals: true,
},
],
"@babel/preset-typescript",
],
plugins: [
[
@@ -134,12 +146,6 @@ module.exports.babelOptions = ({
"@babel/plugin-transform-runtime",
{ version: dependencies["@babel/runtime"] },
],
// Transpile decorators (still in TC39 process)
// Modern browsers support class fields and private methods, but transform is required with the older decorator version dictated by Lit
[
"@babel/plugin-proposal-decorators",
{ version: "2018-09", decoratorsBeforeExport: true },
],
"@babel/plugin-transform-class-properties",
"@babel/plugin-transform-private-methods",
].filter(Boolean),

View File

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

View File

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

View File

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

View File

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

View File

@@ -302,7 +302,7 @@ export class HcConnect extends LitElement {
}
.error {
color: red;
font-weight: bold;
font-weight: var(--ha-font-weight-bold);
}
.error a {

View File

@@ -86,9 +86,9 @@ class HcLayout extends LitElement {
.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);
font-size: var(--ha-card-header-font-size, var(--ha-font-size-2xl));
letter-spacing: -0.012em;
line-height: 32px;
line-height: var(--ha-line-height-normal);
padding: 24px 16px 16px;
display: block;
margin: 0;
@@ -98,7 +98,7 @@ class HcLayout extends LitElement {
border-radius: 4px 4px 0 0;
}
.subtitle {
font-size: 14px;
font-size: var(--ha-font-size-m);
color: var(--secondary-text-color);
line-height: initial;
}
@@ -113,7 +113,7 @@ class HcLayout extends LitElement {
}
:host ::slotted(.section-header) {
font-weight: 500;
font-weight: var(--ha-font-weight-semibold);
padding: 4px 16px;
text-transform: uppercase;
}
@@ -135,7 +135,7 @@ class HcLayout extends LitElement {
.footer {
text-align: center;
font-size: 12px;
font-size: var(--ha-font-size-s);
padding: 8px 0 24px;
color: var(--secondary-text-color);
}

View File

@@ -29,7 +29,7 @@ class HcLaunchScreen extends LitElement {
display: block;
height: 100vh;
background-color: #f2f4f9;
font-size: 24px;
font-size: var(--ha-font-size-2xl);
}
.container {
display: flex;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -50,7 +50,7 @@
font-family: Roboto, Noto, sans-serif;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
font-weight: 400;
font-weight: var(--ha-font-weight-normal);
height: 100vh;
margin: 0;
padding: 0;

View File

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

View File

@@ -37,13 +37,13 @@ class PageDescription extends HaMarkdown {
border-bottom: 1px solid var(--secondary-background-color);
}
.title {
font-size: 42px;
line-height: 56px;
font-size: var(--ha-font-size-5xl);
line-height: var(--ha-line-height-normal);
padding-bottom: 8px;
}
.subtitle {
font-size: 18px;
line-height: 24px;
font-size: var(--ha-font-size-l);
line-height: var(--ha-line-height-normal);
}
.root {
max-width: 800px;

View File

@@ -34,7 +34,7 @@ class HaDemoOptions extends LitElement {
height: 64px;
padding: 0 16px;
pointer-events: none;
font-size: 20px;
font-size: var(--ha-font-size-xl);
}
`,
];

View File

@@ -250,14 +250,14 @@ class HaGallery extends LitElement {
}
.page-footer .header {
font-size: 16px;
font-weight: 500;
line-height: 28px;
font-size: var(--ha-font-size-l);
font-weight: var(--ha-font-weight-semibold);
line-height: var(--ha-line-height-normal);
text-align: center;
}
.page-footer .secondary {
line-height: 23px;
line-height: var(--ha-line-height-normal);
text-align: center;
}

View File

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

View File

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

View File

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

View File

@@ -150,7 +150,7 @@ export class DemoHaBarButton extends LitElement {
margin: 0;
}
label {
font-weight: 600;
font-weight: var(--ha-font-weight-bold);
}
.custom {
--control-button-icon-color: var(--primary-color);

View File

@@ -86,7 +86,7 @@ export class DemoHarControlNumberButtons extends LitElement {
margin: 0;
}
label {
font-weight: 600;
font-weight: var(--ha-font-weight-bold);
}
.custom {
color: #2196f3;

View File

@@ -125,7 +125,7 @@ export class DemoHaControlSelectMenu extends LitElement {
margin: 0;
}
label {
font-weight: 600;
font-weight: var(--ha-font-weight-bold);
}
.custom {
--control-button-icon-color: var(--primary-color);

View File

@@ -181,7 +181,7 @@ export class DemoHaControlSelect extends LitElement {
margin: 0;
}
label {
font-weight: 600;
font-weight: var(--ha-font-weight-bold);
}
.custom {
--mdc-icon-size: 24px;

View File

@@ -144,7 +144,7 @@ export class DemoHaBarSlider extends LitElement {
margin: 0;
}
label {
font-weight: 600;
font-weight: var(--ha-font-weight-bold);
}
.custom {
--control-slider-color: #ffcf4c;

View File

@@ -112,7 +112,7 @@ export class DemoHaControlSwitch extends LitElement {
margin: 0;
}
label {
font-weight: 600;
font-weight: var(--ha-font-weight-bold);
}
.custom {
--control-switch-on-color: var(--green-color);

View File

@@ -105,8 +105,8 @@ export class DemoHaHsColorPicker extends LitElement {
width: 400px;
}
.value {
font-size: 22px;
font-weight: bold;
font-size: var(--ha-font-size-2xl);
font-weight: var(--ha-font-weight-bold);
margin: 0 0 12px 0;
}
`;

View File

@@ -123,7 +123,7 @@ export class DemoHaSelectBox extends LitElement {
margin: 0;
}
label {
font-weight: 600;
font-weight: var(--ha-font-weight-bold);
margin-bottom: 8px;
display: block;
}

View File

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

View File

@@ -18,7 +18,7 @@ Tooltips use `display: contents` so they won't interfere with how elements are p
## Documentation
This element is based on sholace `sl-tooltip` it only sets some css tokens and has a custom show/hide animation.
This element is based on shoelace `sl-tooltip` it only sets some css tokens and has a custom show/hide animation.
<a href="https://shoelace.style/components/tooltip" target="_blank" rel="noopener noreferrer">Shoelace documentation</a>
@@ -28,3 +28,7 @@ In your theme settings use this without the prefixed `--`.
- `--ha-tooltip-border-radius` (Default: 4px)
- `--ha-tooltip-arrow-size` (Default: 8px)
- `--sl-tooltip-font-family` (Default: `var(--ha-font-family-body)`)
- `--ha-tooltip-font-size` (Default: `var(--ha-font-size-s)`)
- `--sl-tooltip-font-weight` (Default: `var(--ha-font-weight-normal)`)
- `--sl-tooltip-line-height` (Default: `var(--ha-line-height-condensed)`)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -85,7 +85,7 @@ class HassioCardContent extends LitElement {
}
ha-svg-icon.hassupdate,
ha-svg-icon.backup {
color: var(--paper-item-icon-color);
color: var(--state-icon-color);
}
ha-svg-icon.not_available {
color: var(--error-color);
@@ -101,7 +101,7 @@ class HassioCardContent extends LitElement {
overflow: hidden;
position: relative;
height: 2.4em;
line-height: 1.2em;
line-height: var(--ha-line-height-condensed);
}
.icon_image img {
max-height: 40px;

View File

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

View File

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

View File

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

View File

@@ -169,8 +169,8 @@ class HassioHardwareDialog extends LitElement {
pre {
padding: 16px;
overflow: auto;
line-height: 1.45;
font-family: var(--code-font-family, monospace);
line-height: var(--ha-line-height-normal);
font-family: var(--ha-font-family-code);
}
code {
font-size: 85%;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import "./hassio-main";
import("../../src/resources/ha-style");
import("../../src/resources/append-ha-style");
const styleEl = document.createElement("style");
styleEl.textContent = `
@@ -8,7 +8,7 @@ body {
font-family: Roboto, sans-serif;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
font-weight: 400;
font-weight: var(--ha-font-weight-normal);
margin: 0;
padding: 0;
height: 100vh;

View File

@@ -340,12 +340,12 @@ class HassioIngressView extends LitElement {
.header {
display: flex;
align-items: center;
font-size: 16px;
font-size: var(--ha-font-size-l);
height: 40px;
padding: 0 16px;
pointer-events: none;
background-color: var(--app-header-background-color);
font-weight: 400;
font-weight: var(--ha-font-weight-normal);
color: var(--app-header-text-color, white);
border-bottom: var(--app-header-border-bottom, none);
box-sizing: border-box;
@@ -354,7 +354,7 @@ class HassioIngressView extends LitElement {
.main-title {
margin: var(--margin-title);
line-height: 20px;
line-height: var(--ha-line-height-normal);
flex-grow: 1;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -31,10 +31,10 @@
"@codemirror/autocomplete": "6.18.6",
"@codemirror/commands": "6.8.1",
"@codemirror/language": "6.11.0",
"@codemirror/legacy-modes": "6.5.0",
"@codemirror/legacy-modes": "6.5.1",
"@codemirror/search": "6.5.10",
"@codemirror/state": "6.5.2",
"@codemirror/view": "6.36.5",
"@codemirror/view": "6.36.6",
"@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "6.18.0",
"@formatjs/intl-displaynames": "6.8.11",
@@ -52,10 +52,11 @@
"@fullcalendar/luxon3": "6.1.17",
"@fullcalendar/timegrid": "6.1.17",
"@lezer/highlight": "1.2.1",
"@lit-labs/context": "0.4.1",
"@lit-labs/motion": "1.0.8",
"@lit-labs/observers": "2.0.5",
"@lit-labs/virtualizer": "2.1.0",
"@lit/context": "1.1.5",
"@lit/reactive-element": "2.1.0",
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
"@material/mwc-base": "0.27.0",
@@ -65,17 +66,15 @@
"@material/mwc-drawer": "0.27.0",
"@material/mwc-fab": "0.27.0",
"@material/mwc-floating-label": "0.27.0",
"@material/mwc-formfield": "0.27.0",
"@material/mwc-formfield": "patch:@material/mwc-formfield@npm%3A0.27.0#~/.yarn/patches/@material-mwc-formfield-npm-0.27.0-9528cb60f6.patch",
"@material/mwc-icon-button": "0.27.0",
"@material/mwc-linear-progress": "0.27.0",
"@material/mwc-list": "0.27.0",
"@material/mwc-list": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
"@material/mwc-menu": "0.27.0",
"@material/mwc-radio": "0.27.0",
"@material/mwc-select": "0.27.0",
"@material/mwc-snackbar": "0.27.0",
"@material/mwc-switch": "0.27.0",
"@material/mwc-tab": "0.27.0",
"@material/mwc-tab-bar": "0.27.0",
"@material/mwc-textarea": "0.27.0",
"@material/mwc-textfield": "0.27.0",
"@material/mwc-top-app-bar": "0.27.0",
@@ -86,11 +85,12 @@
"@mdi/svg": "7.4.47",
"@replit/codemirror-indentation-markers": "6.5.3",
"@shoelace-style/shoelace": "2.20.1",
"@swc/helpers": "0.5.17",
"@thomasloven/round-slider": "0.6.0",
"@tsparticles/engine": "3.8.1",
"@tsparticles/preset-links": "3.2.0",
"@vaadin/combo-box": "24.7.3",
"@vaadin/vaadin-themable-mixin": "24.7.3",
"@vaadin/combo-box": "24.7.4",
"@vaadin/vaadin-themable-mixin": "24.7.4",
"@vibrant/color": "4.0.0",
"@vue/web-component-wrapper": "1.3.0",
"@webcomponents/scoped-custom-element-registry": "0.0.10",
@@ -119,10 +119,10 @@
"leaflet": "1.9.4",
"leaflet-draw": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch",
"leaflet.markercluster": "1.5.3",
"lit": "2.8.0",
"lit-html": "2.8.0",
"lit": "3.3.0",
"lit-html": "3.3.0",
"luxon": "3.6.1",
"marked": "15.0.8",
"marked": "15.0.11",
"memoize-one": "6.0.0",
"node-vibrant": "4.0.3",
"object-hash": "3.0.0",
@@ -152,18 +152,16 @@
"devDependencies": {
"@babel/core": "7.26.10",
"@babel/helper-define-polyfill-provider": "0.6.4",
"@babel/plugin-proposal-decorators": "7.25.9",
"@babel/plugin-transform-runtime": "7.26.10",
"@babel/preset-env": "7.26.9",
"@babel/preset-typescript": "7.27.0",
"@bundle-stats/plugin-webpack-filter": "4.19.1",
"@lokalise/node-api": "14.4.0",
"@octokit/auth-oauth-device": "7.1.5",
"@octokit/plugin-retry": "7.2.1",
"@octokit/rest": "21.1.1",
"@rsdoctor/rspack-plugin": "1.0.2",
"@rspack/cli": "1.3.5",
"@rspack/core": "1.3.5",
"@rspack/cli": "1.3.7",
"@rspack/core": "1.3.7",
"@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.21",
"@types/chromecast-caf-sender": "1.0.11",
@@ -182,12 +180,12 @@
"@types/tar": "6.1.13",
"@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29",
"@vitest/coverage-v8": "3.1.1",
"@vitest/coverage-v8": "3.1.2",
"babel-loader": "10.0.0",
"babel-plugin-template-html-minifier": "4.1.0",
"browserslist-useragent-regexp": "4.1.3",
"del": "8.0.0",
"eslint": "9.25.0",
"eslint": "9.25.1",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-prettier": "10.1.2",
"eslint-import-resolver-webpack": "0.13.10",
@@ -198,7 +196,7 @@
"eslint-plugin-wc": "3.0.0",
"fancy-log": "2.0.0",
"fs-extra": "11.3.0",
"glob": "11.0.1",
"glob": "11.0.2",
"gulp": "5.0.0",
"gulp-brotli": "3.0.0",
"gulp-json-transform": "0.5.0",
@@ -221,22 +219,23 @@
"terser-webpack-plugin": "5.3.14",
"ts-lit-plugin": "2.0.2",
"typescript": "5.8.3",
"typescript-eslint": "8.30.1",
"typescript-eslint": "8.31.0",
"vite-tsconfig-paths": "5.1.4",
"vitest": "3.1.1",
"vitest": "3.1.2",
"webpack-stats-plugin": "1.1.3",
"webpackbar": "7.0.0",
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
},
"resolutions": {
"@material/mwc-button@^0.25.3": "^0.27.0",
"lit": "2.8.0",
"lit-html": "2.8.0",
"lit": "3.3.0",
"lit-html": "3.3.0",
"clean-css": "5.3.3",
"@lit/reactive-element": "1.6.3",
"@lit/reactive-element": "2.1.0",
"@fullcalendar/daygrid": "6.1.17",
"globals": "16.0.0",
"tslib": "2.8.1"
"tslib": "2.8.1",
"@material/mwc-list@^0.27.0": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch"
},
"packageManager": "yarn@4.9.1"
}

View File

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

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

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

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

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

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

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

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -101,7 +101,7 @@ export class HaAuthFlow extends LitElement {
a.forgot-password {
color: var(--primary-color);
text-decoration: none;
font-size: 0.875rem;
font-size: var(--ha-font-size-s);
}
.space-between {
display: flex;

View File

@@ -93,8 +93,8 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
background-color: var(--primary-background-color, #fafafa);
}
p {
font-size: 14px;
line-height: 20px;
font-size: var(--ha-font-size-m);
line-height: var(--ha-line-height-normal);
}
.card-content {
background: var(
@@ -151,8 +151,8 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
margin-inline-start: initial;
}
h1 {
font-size: 28px;
font-weight: 400;
font-size: var(--ha-font-size-3xl);
font-weight: var(--ha-font-weight-normal);
margin-top: 16px;
margin-bottom: 16px;
}

View File

@@ -1,9 +1,9 @@
import "@material/mwc-list";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import type { LocalizeFunc } from "../common/translations/localize";
import "../components/ha-icon-next";
import "../components/ha-list";
import "../components/ha-list-item";
import type { AuthProvider } from "../data/auth";
@@ -29,7 +29,7 @@ export class HaPickAuthProvider extends LitElement {
>${this.localize("ui.panel.page-authorize.pick_auth_provider")}</span
>
</h3>
<mwc-list>
<ha-list>
${this.authProviders.map(
(provider) => html`
<ha-list-item
@@ -43,7 +43,7 @@ export class HaPickAuthProvider extends LitElement {
</ha-list-item>
`
)}
</mwc-list>
</ha-list>
`;
}
@@ -57,9 +57,9 @@ export class HaPickAuthProvider extends LitElement {
position: relative;
z-index: 1;
text-align: center;
font-size: 14px;
font-weight: 400;
line-height: 20px;
font-size: var(--ha-font-size-m);
font-weight: var(--ha-font-weight-normal);
line-height: var(--ha-line-height-normal);
}
h3:before {
border-top: 1px solid var(--divider-color);
@@ -77,7 +77,7 @@ export class HaPickAuthProvider extends LitElement {
background: var(--card-background-color);
padding: 0 15px;
}
mwc-list {
ha-list {
margin: 16px -16px 0;
--mdc-list-side-padding: 24px;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
import type { ThemeVars } from "../../data/ws-themes";
import { darkStyles, derivedStyles } from "../../resources/styles-data";
import { darkColorVariables } from "../../resources/theme/color.globals";
import { derivedStyles } from "../../resources/theme/theme";
import type { HomeAssistant } from "../../types";
import {
hex2rgb,
@@ -50,7 +51,7 @@ export const applyThemesOnElement = (
if (themeToApply && darkMode) {
cacheKey = `${cacheKey}__dark`;
themeRules = { ...darkStyles };
themeRules = { ...darkColorVariables };
}
if (themeToApply === "default") {

View File

@@ -7,6 +7,15 @@ interface AreaContext {
floor: FloorRegistryEntry | null;
}
/**
* Retrieves the context of a specific area, including its associated area registry entry
* and floor registry entry, if available.
*
* @param areaId - The unique identifier of the area to retrieve context for.
* @param hass - The Home Assistant instance containing area and floor registry data.
* @returns An object containing the area registry entry and the associated floor registry entry,
* or `null` values if the area or floor is not found.
*/
export const getAreaContext = (
areaId: string,
hass: HomeAssistant

View File

@@ -11,6 +11,19 @@ interface EntityContext {
floor: FloorRegistryEntry | null;
}
/**
* Retrieves the context of an entity, including its associated device, area, and floor.
*
* @param entityId - The unique identifier of the entity to retrieve the context for.
* @param hass - The Home Assistant object containing the registry data for entities, devices, areas, and floors.
* @returns An object containing the entity, its associated device, area, and floor, or `null` for each if not found.
*
* The returned `EntityContext` object includes:
* - `entity`: The entity registry entry, or `null` if the entity is not found.
* - `device`: The device registry entry associated with the entity, or `null` if not found.
* - `area`: The area registry entry associated with the entity or device, or `null` if not found.
* - `floor`: The floor registry entry associated with the area, or `null` if not found.
*/
export const getEntityContext = (
entityId: string,
hass: HomeAssistant

View File

@@ -96,7 +96,7 @@ const customGenerator = (colors: Swatch[]) => {
// eslint-disable-next-line no-console
console.log(
"%cPicked colors",
`color: ${foregroundColor}; background-color: ${backgroundColor.hex}; font-weight: bold; padding: 16px;`
`color: ${foregroundColor}; background-color: ${backgroundColor.hex}; font-weight: var(--ha-font-weight-bold); padding: 16px;`
);
colors.forEach((color) => logColor(color));
// eslint-disable-next-line no-console

View File

@@ -0,0 +1,45 @@
import type { CSSResult } from "lit";
const _extractCssVars = (
cssString: string,
condition: (string) => boolean = () => true
) => {
const variables: Record<string, string> = {};
cssString.split(";").forEach((rawLine) => {
const line = rawLine.substring(rawLine.indexOf("--")).trim();
if (line.startsWith("--") && condition(line)) {
const [name, value] = line.split(":").map((part) => part.trim());
variables[name.substring(2, name.length)] = value;
}
});
return variables;
};
export const extractVar = (css: CSSResult, varName: string) => {
const cssString = css.toString();
const search = `--${varName}:`;
const startIndex = cssString.indexOf(search);
if (startIndex === -1) {
return "";
}
const endIndex = cssString.indexOf(";", startIndex + search.length);
return cssString.substring(startIndex + search.length, endIndex).trim();
};
export const extractVars = (css: CSSResult) => {
const cssString = css.toString();
return _extractCssVars(cssString);
};
export const extractDerivedVars = (css: CSSResult) => {
const cssString = css.toString();
if (!cssString.includes("var(")) {
return {};
}
return _extractCssVars(cssString, (line) => line.includes("var("));
};

View File

@@ -1,4 +1,4 @@
import { consume } from "@lit-labs/context";
import { consume } from "@lit/context";
import { ResizeController } from "@lit-labs/observers/resize-controller";
import { mdiChevronDown, mdiChevronUp, mdiRestart } from "@mdi/js";
import { differenceInMinutes } from "date-fns";
@@ -590,6 +590,10 @@ export class HaChartBase extends LitElement {
lineStyle: { color: style.getPropertyValue("--info-color") },
crossStyle: { color: style.getPropertyValue("--info-color") },
},
extraCssText:
"direction:" +
style.getPropertyValue("--direction") +
";margin-inline-start:3px;margin-inline-end:8px;",
},
timeline: {},
};
@@ -715,7 +719,7 @@ export class HaChartBase extends LitElement {
max-height: 60%;
overflow-y: auto;
padding: 12px 0 0;
font-size: 12px;
font-size: var(--ha-font-size-s);
color: var(--primary-text-color);
}
.chart-legend ul {

View File

@@ -135,7 +135,7 @@ export class StateHistoryChartLine extends LitElement {
seriesIndex: index,
value: lastData,
// HTML copied from echarts. May change based on options
marker: `<span style="display:inline-block;margin-right:4px;border-radius:10px;width:10px;height:10px;background-color:${dataset.color};"></span>`,
marker: `<span style="display:inline-block;margin-right:4px;margin-inline-end:4px;margin-inline-start:initial;border-radius:10px;width:10px;height:10px;background-color:${dataset.color};"></span>`,
});
});
const unit = this.unit

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