Compare commits

..

1 Commits

Author SHA1 Message Date
Paulus Schoutsen
3b69f9cc8d Update Z-Wave data type
Data added in https://github.com/home-assistant/core/pull/117288/files
2024-05-12 22:24:11 -04:00
308 changed files with 5746 additions and 9095 deletions

View File

@@ -115,7 +115,6 @@
}
],
"unused-imports/no-unused-imports": "error",
"lit/attribute-names": "warn",
"lit/attribute-value-entities": "off",
"lit/no-template-map": "off",
"lit/no-native-attributes": "warn",
@@ -126,5 +125,6 @@
"lit-a11y/anchor-is-valid": "warn",
"lit-a11y/role-has-required-aria-attrs": "warn"
},
"plugins": ["unused-imports"]
"plugins": ["disable", "unused-imports"],
"processor": "disable/disable"
}

View File

@@ -21,7 +21,7 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.1.4
with:
ref: dev
@@ -57,7 +57,7 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.1.4
with:
ref: master

View File

@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.1.4
- name: Setup Node
uses: actions/setup-node@v4.0.2
with:
@@ -58,7 +58,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.1.4
- name: Setup Node
uses: actions/setup-node@v4.0.2
with:
@@ -76,7 +76,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.1.4
- name: Setup Node
uses: actions/setup-node@v4.0.2
with:
@@ -100,7 +100,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.1.4
- name: Setup Node
uses: actions/setup-node@v4.0.2
with:

View File

@@ -23,7 +23,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.1.4
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.

View File

@@ -22,7 +22,7 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.1.4
with:
ref: dev
@@ -58,7 +58,7 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.1.4
with:
ref: master

View File

@@ -16,7 +16,7 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.1.4
- name: Setup Node
uses: actions/setup-node@v4.0.2

View File

@@ -21,7 +21,7 @@ jobs:
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.1.4
- name: Setup Node
uses: actions/setup-node@v4.0.2

View File

@@ -20,7 +20,7 @@ jobs:
contents: write
steps:
- name: Checkout the repository
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.1.4
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5

View File

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

View File

@@ -23,7 +23,7 @@ jobs:
contents: write # Required to upload release assets
steps:
- name: Checkout the repository
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.1.4
- name: Verify version
uses: home-assistant/actions/helpers/verify-version@master
@@ -55,7 +55,7 @@ jobs:
script/release
- name: Upload release assets
uses: softprops/action-gh-release@v2.0.6
uses: softprops/action-gh-release@v2.0.4
with:
files: |
dist/*.whl

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.1.4
- name: Upload Translations
run: |

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -1,56 +1,7 @@
import defineProvider from "@babel/helper-define-polyfill-provider";
import { join } from "node:path";
import paths from "../paths.cjs";
const POLYFILL_DIR = join(paths.polymer_dir, "src/resources/polyfills");
// List of polyfill keys with supported browser targets for the functionality
const PolyfillSupport = {
// Note states and shadowRoot properties should be supported.
"element-internals": {
android: 90,
chrome: 90,
edge: 90,
firefox: 126,
ios: 17.4,
opera: 76,
opera_mobile: 64,
safari: 17.4,
samsung: 15.0,
},
"element-append": {
android: 54,
chrome: 54,
edge: 17,
firefox: 49,
ios: 10.0,
opera: 41,
opera_mobile: 41,
safari: 10.0,
samsung: 6.0,
},
"element-getattributenames": {
android: 61,
chrome: 61,
edge: 18,
firefox: 45,
ios: 10.3,
opera: 48,
opera_mobile: 45,
safari: 10.1,
samsung: 8.0,
},
"element-toggleattribute": {
android: 69,
chrome: 69,
edge: 18,
firefox: 63,
ios: 12.0,
opera: 56,
opera_mobile: 48,
safari: 12.0,
samsung: 10.0,
},
fetch: {
android: 42,
chrome: 42,
@@ -62,31 +13,6 @@ const PolyfillSupport = {
safari: 10.1,
samsung: 4.0,
},
"intl-getcanonicallocales": {
android: 54,
chrome: 54,
edge: 16,
firefox: 48,
ios: 10.3,
opera: 41,
opera_mobile: 41,
safari: 10.1,
samsung: 6.0,
},
"intl-locale": {
android: 74,
chrome: 74,
edge: 79,
firefox: 75,
ios: 14.0,
opera: 62,
opera_mobile: 53,
safari: 14.0,
samsung: 11.0,
},
"intl-other": {
// Not specified (i.e. always try polyfill) since compatibility depends on supported locales
},
proxy: {
android: 49,
chrome: 49,
@@ -98,67 +24,17 @@ const PolyfillSupport = {
safari: 10.0,
samsung: 5.0,
},
"resize-observer": {
android: 64,
chrome: 64,
edge: 79,
firefox: 69,
ios: 13.4,
opera: 51,
opera_mobile: 47,
safari: 13.1,
samsung: 9.0,
},
};
// Map of global variables and/or instance and static properties to the
// corresponding polyfill key and actual module to import
const polyfillMap = {
global: {
fetch: { key: "fetch", module: "unfetch/polyfill" },
Proxy: { key: "proxy", module: "proxy-polyfill" },
ResizeObserver: {
key: "resize-observer",
module: join(POLYFILL_DIR, "resize-observer.ts"),
},
},
instance: {
attachInternals: {
key: "element-internals",
module: "element-internals-polyfill",
},
...Object.fromEntries(
["append", "getAttributeNames", "toggleAttribute"].map((prop) => {
const key = `element-${prop.toLowerCase()}`;
return [prop, { key, module: join(POLYFILL_DIR, `${key}.ts`) }];
})
),
},
static: {
Intl: {
getCanonicalLocales: {
key: "intl-getcanonicallocales",
module: join(POLYFILL_DIR, "intl-polyfill.ts"),
},
Locale: {
key: "intl-locale",
module: join(POLYFILL_DIR, "intl-polyfill.ts"),
},
...Object.fromEntries(
[
"DateTimeFormat",
"DisplayNames",
"ListFormat",
"NumberFormat",
"PluralRules",
"RelativeTimeFormat",
].map((obj) => [
obj,
{ key: "intl-other", module: join(POLYFILL_DIR, "intl-polyfill.ts") },
])
),
},
fetch: { key: "fetch", module: "unfetch/polyfill" },
},
instance: {},
static: {},
};
// Create plugin using the same factory as for CoreJS
@@ -166,16 +42,14 @@ export default defineProvider(
({ createMetaResolver, debug, shouldInjectPolyfill }) => {
const resolvePolyfill = createMetaResolver(polyfillMap);
return {
name: "custom-polyfill",
name: "HA Custom",
polyfills: PolyfillSupport,
usageGlobal(meta, utils) {
const polyfill = resolvePolyfill(meta);
if (polyfill && shouldInjectPolyfill(polyfill.desc.key)) {
debug(polyfill.desc.key);
utils.injectGlobalImport(polyfill.desc.module);
return true;
}
return false;
},
};
}

View File

@@ -92,8 +92,8 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
[
"@babel/preset-env",
{
useBuiltIns: "usage",
corejs: dependencies["core-js"],
useBuiltIns: latestBuild ? false : "usage",
corejs: latestBuild ? false : dependencies["core-js"],
bugfixes: true,
shippedProposals: true,
},
@@ -147,24 +147,14 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
overrides: [
{
// Add plugin to inject various polyfills, excluding the polyfills
// themselves to prevent self-injection.
// themselves to prevent self- injection.
plugins: [
[
path.join(BABEL_PLUGINS, "custom-polyfill-plugin.js"),
{ method: "usage-global" },
],
],
exclude: [
path.join(paths.polymer_dir, "src/resources/polyfills"),
...[
"@formatjs/(?:ecma402-abstract|intl-\\w+)",
"@lit-labs/virtualizer/polyfills",
"@webcomponents/scoped-custom-element-registry",
"element-internals-polyfill",
"proxy-polyfill",
"unfetch",
].map((p) => new RegExp(`/node_modules/${p}/`)),
],
exclude: /\/node_modules\/(?:unfetch|proxy-polyfill)\//,
},
{
// Use unambiguous for dependencies so that require() is correctly injected into CommonJS files

View File

@@ -19,7 +19,6 @@ const inBackendDir = "translations/backend";
const workDir = "build/translations";
const outDir = join(workDir, "output");
const EN_SRC = join(paths.translations_src, "en.json");
const TEST_LOCALE = "en-x-test";
let mergeBackend = false;
@@ -151,7 +150,7 @@ const createTestTranslation = () =>
: gulp
.src(EN_SRC)
.pipe(new CustomJSON(null, testReviver))
.pipe(rename(`${TEST_LOCALE}.json`))
.pipe(rename("test.json"))
.pipe(gulp.dest(workDir));
/**
@@ -193,7 +192,7 @@ const createTranslations = async () => {
// each locale, then fragmentizes and flattens the data for final output.
const translationFiles = await glob([
`${inFrontendDir}/!(en).json`,
...(env.isProdBuild() ? [] : [`${workDir}/${TEST_LOCALE}.json`]),
...(env.isProdBuild() ? [] : [`${workDir}/test.json`]),
]);
const hashStream = new Transform({
objectMode: true,
@@ -255,8 +254,8 @@ const createTranslations = async () => {
const mergeFiles = [];
for (let i = 1; i <= subtags.length; i++) {
const lang = subtags.slice(0, i).join("-");
if (lang === TEST_LOCALE) {
mergeFiles.push(`${workDir}/${TEST_LOCALE}.json`);
if (lang === "test") {
mergeFiles.push(`${workDir}/test.json`);
} else if (lang !== "en") {
mergeFiles.push(`${inFrontendDir}/${lang}.json`);
if (mergeBackend) {
@@ -285,7 +284,7 @@ const writeTranslationMetaData = () =>
new CustomJSON((meta) => {
// Add the test translation in development.
if (!env.isProdBuild()) {
meta[TEST_LOCALE] = { nativeName: "Translation Test" };
meta.test = { nativeName: "Test" };
}
// Filter out locales without a native name, and add the hashes.
for (const locale of Object.keys(meta)) {

View File

@@ -1,6 +1,7 @@
import "@material/mwc-button/mwc-button";
import { ActionDetail } from "@material/mwc-list/mwc-list";
import { mdiCast, mdiCastConnected, mdiViewDashboard } from "@mdi/js";
import { mdiCast, mdiCastConnected } from "@mdi/js";
import "@polymer/paper-item/paper-icon-item";
import "@polymer/paper-listbox/paper-listbox";
import { Auth, Connection } from "home-assistant-js-websocket";
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
import { customElement, property, state } from "lit/decorators";
@@ -27,7 +28,6 @@ import { LovelaceViewConfig } from "../../../../src/data/lovelace/config/view";
import "../../../../src/layouts/hass-loading-screen";
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
import "./hc-layout";
import "../../../../src/components/ha-list-item";
@customElement("hc-cast")
class HcCast extends LitElement {
@@ -83,37 +83,34 @@ class HcCast extends LitElement {
`
: html`
<div class="section-header">PICK A VIEW</div>
<mwc-list @action=${this._handlePickView} activatable>
<paper-listbox
attr-for-selected="data-path"
.selected=${this.castManager.status.lovelacePath || ""}
>
${(
this.lovelaceViews ?? [
generateDefaultViewConfig({}, {}, {}, {}, () => ""),
]
).map(
(view, idx) =>
html`<ha-list-item
graphic="avatar"
.activated=${this.castManager.status?.lovelacePath ===
(view.path ?? idx)}
.selected=${this.castManager.status?.lovelacePath ===
(view.path ?? idx)}
(view, idx) => html`
<paper-icon-item
@click=${this._handlePickView}
data-path=${view.path || idx}
>
${view.title || view.path || "Unnamed view"}
${view.icon
? html`
<ha-icon
.icon=${view.icon}
slot="graphic"
slot="item-icon"
></ha-icon>
`
: html`<ha-svg-icon
slot="item-icon"
.path=${mdiViewDashboard}
></ha-svg-icon>`}</ha-list-item
> `
)}</mwc-list
>
: ""}
${view.title || view.path}
</paper-icon-item>
`
)}
</paper-listbox>
`}
<div class="card-actions">
${this.castManager.status
? html`
@@ -185,8 +182,8 @@ class HcCast extends LitElement {
this.castManager.requestSession();
}
private async _handlePickView(ev: CustomEvent<ActionDetail>) {
const path = this.lovelaceViews![ev.detail.index].path ?? ev.detail.index;
private async _handlePickView(ev: Event) {
const path = (ev.currentTarget as any).getAttribute("data-path");
await ensureConnectedCastSession(this.castManager!, this.auth!);
castSendShowLovelaceView(this.castManager, this.auth.data.hassUrl, path);
}
@@ -249,14 +246,25 @@ class HcCast extends LitElement {
height: 18px;
}
ha-list-item ha-icon,
ha-list-item ha-svg-icon {
paper-listbox {
padding-top: 0;
}
paper-listbox ha-icon {
padding: 12px;
color: var(--secondary-text-color);
}
:host([hide-icons]) ha-icon {
display: none;
paper-icon-item {
cursor: pointer;
}
paper-icon-item[disabled] {
cursor: initial;
}
:host([hide-icons]) paper-icon-item {
--paper-item-icon-width: 0px;
}
.spacer {

View File

@@ -2,7 +2,6 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
import { getPanelTitleFromUrlPath } from "../../../../src/data/panel";
import { Lovelace } from "../../../../src/panels/lovelace/types";
import "../../../../src/panels/lovelace/views/hui-view";
import { HomeAssistant } from "../../../../src/types";
@@ -62,12 +61,7 @@ class HcLovelace extends LitElement {
const index = this._viewIndex;
if (index !== undefined) {
const title = getPanelTitleFromUrlPath(
this.hass,
this.urlPath || "lovelace"
);
const dashboardTitle = title || this.urlPath;
const dashboardTitle = this.lovelaceConfig.title || this.urlPath;
const viewTitle =
this.lovelaceConfig.views[index].title ||
@@ -86,17 +80,10 @@ class HcLovelace extends LitElement {
this.lovelaceConfig.views[index].background ||
this.lovelaceConfig.background;
const backgroundStyle =
typeof configBackground === "string"
? configBackground
: configBackground?.image
? `center / cover no-repeat url('${configBackground.image}')`
: undefined;
if (backgroundStyle) {
if (configBackground) {
this._huiView!.style.setProperty(
"--lovelace-background",
backgroundStyle
configBackground
);
} else {
this._huiView!.style.removeProperty("--lovelace-background");

View File

@@ -35,7 +35,6 @@ import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/lo
import { HassElement } from "../../../../src/state/hass-element";
import { castContext } from "../cast_context";
import "./hc-launch-screen";
import { getPanelTitleFromUrlPath } from "../../../../src/data/panel";
const DEFAULT_CONFIG: LovelaceDashboardStrategyConfig = {
strategy: {
@@ -360,11 +359,7 @@ export class HcMain extends HassElement {
}
private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) {
const title = getPanelTitleFromUrlPath(
this.hass!,
this._urlPath || "lovelace"
);
castContext.setApplicationState(title || "");
castContext.setApplicationState(lovelaceConfig.title || "");
this._lovelaceConfig = lovelaceConfig;
}

View File

@@ -1,9 +1,8 @@
import "@material/mwc-button";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { until } from "lit/directives/until";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/ha-card";
import "../../../src/components/ha-button";
import "../../../src/components/ha-circular-progress";
import { LovelaceCardConfig } from "../../../src/data/lovelace/config/card";
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
@@ -12,6 +11,7 @@ import {
demoConfigs,
selectedDemoConfig,
selectedDemoConfigIndex,
setDemoConfig,
} from "../configs/demo-configs";
@customElement("ha-demo-card")
@@ -64,9 +64,9 @@ export class HADemoCard extends LitElement implements LovelaceCard {
)}
</div>
<ha-button @click=${this._nextConfig} .disabled=${this._switching}>
<mwc-button @click=${this._nextConfig} .disabled=${this._switching}>
${this.hass.localize("ui.panel.page-demo.cards.demo.next_demo")}
</ha-button>
</mwc-button>
</div>
<div class="content">
<p class="small-hidden">
@@ -87,9 +87,9 @@ export class HADemoCard extends LitElement implements LovelaceCard {
</div>
<div class="actions small-hidden">
<a href="https://www.home-assistant.io" target="_blank">
<ha-button>
<mwc-button>
${this.hass.localize("ui.panel.page-demo.cards.demo.learn_more")}
</ha-button>
</mwc-button>
</a>
</div>
</ha-card>
@@ -113,7 +113,13 @@ export class HADemoCard extends LitElement implements LovelaceCard {
private async _updateConfig(index: number) {
this._switching = true;
fireEvent(this, "set-demo-config" as any, { index });
try {
await setDemoConfig(this.hass, this.lovelace!, index);
} catch (err: any) {
alert("Failed to switch config :-(");
} finally {
this._switching = false;
}
}
static get styles(): CSSResultGroup {
@@ -143,7 +149,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
height: 60px;
}
.picker ha-button {
.picker mwc-button {
margin-right: 8px;
}

View File

@@ -1,12 +1,9 @@
import type { LocalizeFunc } from "../../../src/common/translations/localize";
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
import {
selectedDemoConfig,
selectedDemoConfigIndex,
setDemoConfig,
} from "../configs/demo-configs";
import { selectedDemoConfig } from "../configs/demo-configs";
import "../custom-cards/cast-demo-row";
import "../custom-cards/ha-demo-card";
import type { HADemoCard } from "../custom-cards/ha-demo-card";
export const mockLovelace = (
hass: MockHomeAssistant,
@@ -22,22 +19,17 @@ export const mockLovelace = (
hass.mockWS("lovelace/resources", () => Promise.resolve([]));
};
customElements.whenDefined("hui-root").then(() => {
customElements.whenDefined("hui-view").then(() => {
// eslint-disable-next-line
const HUIRoot = customElements.get("hui-root")!;
const HUIView = customElements.get("hui-view");
// Patch HUI-VIEW to make the lovelace object available to the demo card
const oldCreateCard = HUIView!.prototype.createCardElement;
const oldFirstUpdated = HUIRoot.prototype.firstUpdated;
HUIRoot.prototype.firstUpdated = function (changedProperties) {
oldFirstUpdated.call(this, changedProperties);
this.addEventListener("set-demo-config", async (ev) => {
const index = (ev as CustomEvent).detail.index;
try {
await setDemoConfig(this.hass, this.lovelace!, index);
} catch (err: any) {
setDemoConfig(this.hass, this.lovelace!, selectedDemoConfigIndex);
alert("Failed to switch config :-(");
}
});
HUIView!.prototype.createCardElement = function (config) {
const el = oldCreateCard.call(this, config);
if (el.tagName === "HA-DEMO-CARD") {
(el as HADemoCard).lovelace = this.lovelace;
}
return el;
};
});

View File

@@ -1,9 +1,7 @@
import { load } from "js-yaml";
import { LitElement, PropertyValueMap, css, html, nothing } from "lit";
import { html, css, LitElement, PropertyValues } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import "../../../src/panels/lovelace/cards/hui-card";
import type { HuiCard } from "../../../src/panels/lovelace/cards/hui-card";
import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element";
import { HomeAssistant } from "../../../src/types";
export interface DemoCardConfig {
@@ -21,12 +19,7 @@ class DemoCard extends LitElement {
@state() private _size?: number;
@query("hui-card", false) private _card?: HuiCard;
private _config = memoizeOne((config: string) => {
const c = (load(config) as any)[0];
return c;
});
@query("#card") private _card!: HTMLElement;
render() {
return html`
@@ -37,32 +30,63 @@ class DemoCard extends LitElement {
: ""}
</h2>
<div class="root">
<hui-card
.config=${this._config(this.config.config)}
.hass=${this.hass}
@card-updated=${this._cardUpdated}
></hui-card>
${this.showConfig
? html`<pre>${this.config.config.trim()}</pre>`
: nothing}
<div id="card"></div>
${this.showConfig ? html`<pre>${this.config.config.trim()}</pre>` : ""}
</div>
`;
}
private async _cardUpdated(ev) {
ev.stopPropagation();
this._updateSize();
updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (changedProps.has("config")) {
const card = this._card;
while (card.lastChild) {
card.removeChild(card.lastChild);
}
const el = this._createCardElement((load(this.config.config) as any)[0]);
card.appendChild(el);
this._getSize(el);
}
if (changedProps.has("hass")) {
const card = this._card.lastChild;
if (card) {
(card as any).hass = this.hass;
}
}
}
private async _updateSize() {
this._size = await this._card?.getCardSize();
async _getSize(el) {
await customElements.whenDefined(el.localName);
if (!("getCardSize" in el)) {
this._size = undefined;
return;
}
this._size = await el.getCardSize();
}
protected update(
_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>
): void {
super.update(_changedProperties);
this._updateSize();
_createCardElement(cardConfig) {
const element = createCardElement(cardConfig);
if (this.hass) {
element.hass = this.hass;
}
element.addEventListener(
"ll-rebuild",
(ev) => {
ev.stopPropagation();
this._rebuildCard(element, cardConfig);
},
{ once: true }
);
return element;
}
_rebuildCard(cardElToReplace, config) {
const newCardEl = this._createCardElement(config);
cardElToReplace.parentElement.replaceChild(newCardEl, cardElToReplace);
}
static styles = css`
@@ -77,7 +101,7 @@ class DemoCard extends LitElement {
font-size: 0.5em;
color: var(--primary-text-color);
}
hui-card {
#card {
max-width: 400px;
width: 100vw;
}

View File

@@ -64,12 +64,6 @@ const ACTIONS = [
entity_id: "input_boolean.toggle_4",
},
},
{
sequence: [
{ scene: "scene.kitchen_morning" },
{ service: "light.turn_off", target: { entity_id: "light.kitchen" } },
],
},
{
parallel: [
{ scene: "scene.kitchen_morning" },

View File

@@ -20,7 +20,6 @@ import { HaWaitForTriggerAction } from "../../../../src/panels/config/automation
import { HaWaitAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-wait_template";
import { 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";
@@ -40,7 +39,6 @@ const SCHEMAS: { name: string; actions: Action[] }[] = [
{ name: "If-Then", actions: [HaIfAction.defaultConfig] },
{ name: "Choose", actions: [HaChooseAction.defaultConfig] },
{ name: "Variables", actions: [{ variables: { hello: "1" } }] },
{ name: "Sequence", actions: [HaSequenceAction.defaultConfig] },
{ name: "Parallel", actions: [HaParallelAction.defaultConfig] },
{ name: "Stop", actions: [HaStopAction.defaultConfig] },
];

View File

@@ -56,46 +56,48 @@ export class DemoDateTimeDateTimeNumeric extends LitElement {
<div class="center">12 Hours</div>
<div class="center">24 Hours</div>
</div>
${Object.entries(translationMetadata.translations).map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatDateTimeNumeric(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
${Object.entries(translationMetadata.translations)
.filter(([key, _]) => key !== "test")
.map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatDateTimeNumeric(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
</div>
<div class="center">
${formatDateTimeNumeric(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatDateTimeNumeric(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
<div class="center">
${formatDateTimeNumeric(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatDateTimeNumeric(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
`
)}
`
)}
</mwc-list>
`;
}

View File

@@ -56,46 +56,48 @@ export class DemoDateTimeDateTimeSeconds extends LitElement {
<div class="center">12 Hours</div>
<div class="center">24 Hours</div>
</div>
${Object.entries(translationMetadata.translations).map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatDateTimeWithSeconds(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
${Object.entries(translationMetadata.translations)
.filter(([key, _]) => key !== "test")
.map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatDateTimeWithSeconds(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
</div>
<div class="center">
${formatDateTimeWithSeconds(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatDateTimeWithSeconds(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
<div class="center">
${formatDateTimeWithSeconds(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatDateTimeWithSeconds(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
`
)}
`
)}
</mwc-list>
`;
}

View File

@@ -56,46 +56,48 @@ export class DemoDateTimeDateTimeShortYear extends LitElement {
<div class="center">12 Hours</div>
<div class="center">24 Hours</div>
</div>
${Object.entries(translationMetadata.translations).map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatShortDateTimeWithYear(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
${Object.entries(translationMetadata.translations)
.filter(([key, _]) => key !== "test")
.map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatShortDateTimeWithYear(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
</div>
<div class="center">
${formatShortDateTimeWithYear(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatShortDateTimeWithYear(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
<div class="center">
${formatShortDateTimeWithYear(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatShortDateTimeWithYear(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
`
)}
`
)}
</mwc-list>
`;
}

View File

@@ -56,46 +56,48 @@ export class DemoDateTimeDateTimeShort extends LitElement {
<div class="center">12 Hours</div>
<div class="center">24 Hours</div>
</div>
${Object.entries(translationMetadata.translations).map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatShortDateTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
${Object.entries(translationMetadata.translations)
.filter(([key, _]) => key !== "test")
.map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatShortDateTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
</div>
<div class="center">
${formatShortDateTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatShortDateTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
<div class="center">
${formatShortDateTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatShortDateTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
`
)}
`
)}
</mwc-list>
`;
}

View File

@@ -56,46 +56,48 @@ export class DemoDateTimeDateTime extends LitElement {
<div class="center">12 Hours</div>
<div class="center">24 Hours</div>
</div>
${Object.entries(translationMetadata.translations).map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatDateTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
${Object.entries(translationMetadata.translations)
.filter(([key, _]) => key !== "test")
.map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatDateTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
</div>
<div class="center">
${formatDateTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatDateTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
<div class="center">
${formatDateTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatDateTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
`
)}
`
)}
</mwc-list>
`;
}

View File

@@ -35,57 +35,59 @@ export class DemoDateTimeDate extends LitElement {
<div class="center">Month-Day-Year</div>
<div class="center">Year-Month-Day</div>
</div>
${Object.entries(translationMetadata.translations).map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatDateNumeric(
date,
{
...defaultLocale,
language: key,
date_format: DateFormat.language,
},
demoConfig
)}
${Object.entries(translationMetadata.translations)
.filter(([key, _]) => key !== "test")
.map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatDateNumeric(
date,
{
...defaultLocale,
language: key,
date_format: DateFormat.language,
},
demoConfig
)}
</div>
<div class="center">
${formatDateNumeric(
date,
{
...defaultLocale,
language: key,
date_format: DateFormat.DMY,
},
demoConfig
)}
</div>
<div class="center">
${formatDateNumeric(
date,
{
...defaultLocale,
language: key,
date_format: DateFormat.MDY,
},
demoConfig
)}
</div>
<div class="center">
${formatDateNumeric(
date,
{
...defaultLocale,
language: key,
date_format: DateFormat.YMD,
},
demoConfig
)}
</div>
</div>
<div class="center">
${formatDateNumeric(
date,
{
...defaultLocale,
language: key,
date_format: DateFormat.DMY,
},
demoConfig
)}
</div>
<div class="center">
${formatDateNumeric(
date,
{
...defaultLocale,
language: key,
date_format: DateFormat.MDY,
},
demoConfig
)}
</div>
<div class="center">
${formatDateNumeric(
date,
{
...defaultLocale,
language: key,
date_format: DateFormat.YMD,
},
demoConfig
)}
</div>
</div>
`
)}
`
)}
</mwc-list>
`;
}

View File

@@ -56,46 +56,48 @@ export class DemoDateTimeTimeSeconds extends LitElement {
<div class="center">12 Hours</div>
<div class="center">24 Hours</div>
</div>
${Object.entries(translationMetadata.translations).map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatTimeWithSeconds(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
${Object.entries(translationMetadata.translations)
.filter(([key, _]) => key !== "test")
.map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatTimeWithSeconds(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
</div>
<div class="center">
${formatTimeWithSeconds(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatTimeWithSeconds(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
<div class="center">
${formatTimeWithSeconds(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatTimeWithSeconds(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
`
)}
`
)}
</mwc-list>
`;
}

View File

@@ -56,46 +56,48 @@ export class DemoDateTimeTimeWeekday extends LitElement {
<div class="center">12 Hours</div>
<div class="center">24 Hours</div>
</div>
${Object.entries(translationMetadata.translations).map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatTimeWeekday(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
${Object.entries(translationMetadata.translations)
.filter(([key, _]) => key !== "test")
.map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatTimeWeekday(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
</div>
<div class="center">
${formatTimeWeekday(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatTimeWeekday(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
<div class="center">
${formatTimeWeekday(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatTimeWeekday(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
`
)}
`
)}
</mwc-list>
`;
}

View File

@@ -56,46 +56,48 @@ export class DemoDateTimeTime extends LitElement {
<div class="center">12 Hours</div>
<div class="center">24 Hours</div>
</div>
${Object.entries(translationMetadata.translations).map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
${Object.entries(translationMetadata.translations)
.filter(([key, _]) => key !== "test")
.map(
([key, value]) => html`
<div class="container">
<div>${value.nativeName}</div>
<div class="center">
${formatTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.language,
},
demoConfig
)}
</div>
<div class="center">
${formatTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
<div class="center">
${formatTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.am_pm,
},
demoConfig
)}
</div>
<div class="center">
${formatTime(
this.date,
{
...defaultLocale,
language: key,
time_format: TimeFormat.twenty_four,
},
demoConfig
)}
</div>
</div>
`
)}
`
)}
</mwc-list>
`;
}

View File

@@ -368,7 +368,6 @@ export class DemoEntityState extends LitElement {
hass.localize,
entry.stateObj,
hass.locale,
[], // numericDeviceClasses
hass.config,
hass.entities
)}`,

View File

@@ -25,24 +25,24 @@
"license": "Apache-2.0",
"type": "module",
"dependencies": {
"@babel/runtime": "7.24.7",
"@braintree/sanitize-url": "7.0.3",
"@codemirror/autocomplete": "6.16.3",
"@codemirror/commands": "6.6.0",
"@codemirror/language": "6.10.2",
"@babel/runtime": "7.24.5",
"@braintree/sanitize-url": "7.0.1",
"@codemirror/autocomplete": "6.16.0",
"@codemirror/commands": "6.5.0",
"@codemirror/language": "6.10.1",
"@codemirror/legacy-modes": "6.4.0",
"@codemirror/search": "6.5.6",
"@codemirror/state": "6.4.1",
"@codemirror/view": "6.28.2",
"@codemirror/view": "6.26.3",
"@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "6.12.5",
"@formatjs/intl-displaynames": "6.6.8",
"@formatjs/intl-datetimeformat": "6.12.3",
"@formatjs/intl-displaynames": "6.6.6",
"@formatjs/intl-getcanonicallocales": "2.3.0",
"@formatjs/intl-listformat": "7.5.7",
"@formatjs/intl-locale": "4.0.0",
"@formatjs/intl-numberformat": "8.10.3",
"@formatjs/intl-pluralrules": "5.2.14",
"@formatjs/intl-relativetimeformat": "11.2.14",
"@formatjs/intl-listformat": "7.5.5",
"@formatjs/intl-locale": "3.4.5",
"@formatjs/intl-numberformat": "8.10.1",
"@formatjs/intl-pluralrules": "5.2.12",
"@formatjs/intl-relativetimeformat": "11.2.12",
"@fullcalendar/core": "6.1.11",
"@fullcalendar/daygrid": "6.1.11",
"@fullcalendar/interaction": "6.1.11",
@@ -53,7 +53,7 @@
"@lit-labs/context": "0.4.1",
"@lit-labs/motion": "1.0.7",
"@lit-labs/observers": "2.0.2",
"@lit-labs/virtualizer": "2.0.13",
"@lit-labs/virtualizer": "2.0.12",
"@lrnwebcomponents/simple-tooltip": "8.0.2",
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
@@ -80,7 +80,7 @@
"@material/mwc-top-app-bar": "0.27.0",
"@material/mwc-top-app-bar-fixed": "0.27.0",
"@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
"@material/web": "1.5.0",
"@material/web": "1.4.1",
"@mdi/js": "7.4.47",
"@mdi/svg": "7.4.47",
"@polymer/paper-item": "3.0.1",
@@ -88,8 +88,8 @@
"@polymer/paper-tabs": "3.1.0",
"@polymer/polymer": "3.5.1",
"@thomasloven/round-slider": "0.6.0",
"@vaadin/combo-box": "24.4.0",
"@vaadin/vaadin-themable-mixin": "24.4.0",
"@vaadin/combo-box": "24.3.12",
"@vaadin/vaadin-themable-mixin": "24.3.12",
"@vibrant/color": "3.2.1-alpha.1",
"@vibrant/core": "3.2.1-alpha.1",
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
@@ -97,10 +97,10 @@
"@webcomponents/scoped-custom-element-registry": "0.0.9",
"@webcomponents/webcomponentsjs": "2.8.0",
"app-datepicker": "5.1.1",
"chart.js": "4.4.3",
"chart.js": "4.4.2",
"color-name": "2.0.0",
"comlink": "4.4.1",
"core-js": "3.37.1",
"core-js": "3.37.0",
"cropperjs": "1.6.2",
"date-fns": "3.6.0",
"date-fns-tz": "3.1.3",
@@ -110,9 +110,9 @@
"fuse.js": "7.0.0",
"google-timezones-json": "1.2.0",
"hls.js": "patch:hls.js@npm%3A1.5.7#~/.yarn/patches/hls.js-npm-1.5.7-f5bbd3d060.patch",
"home-assistant-js-websocket": "9.4.0",
"home-assistant-js-websocket": "9.3.0",
"idb-keyval": "6.2.1",
"intl-messageformat": "10.5.14",
"intl-messageformat": "10.5.12",
"js-yaml": "4.1.0",
"leaflet": "1.9.4",
"leaflet-draw": "1.0.4",
@@ -133,7 +133,7 @@
"tinykeys": "2.1.0",
"tsparticles-engine": "2.12.0",
"tsparticles-preset-links": "2.12.0",
"ua-parser-js": "1.0.38",
"ua-parser-js": "1.0.37",
"unfetch": "5.0.0",
"vis-data": "7.1.9",
"vis-network": "9.1.9",
@@ -149,27 +149,27 @@
"xss": "1.0.15"
},
"devDependencies": {
"@babel/core": "7.24.7",
"@babel/core": "7.24.5",
"@babel/helper-define-polyfill-provider": "0.6.2",
"@babel/plugin-proposal-decorators": "7.24.7",
"@babel/plugin-transform-runtime": "7.24.7",
"@babel/preset-env": "7.24.7",
"@babel/preset-typescript": "7.24.7",
"@bundle-stats/plugin-webpack-filter": "4.13.2",
"@babel/plugin-proposal-decorators": "7.24.1",
"@babel/plugin-transform-runtime": "7.24.3",
"@babel/preset-env": "7.24.5",
"@babel/preset-typescript": "7.24.1",
"@bundle-stats/plugin-webpack-filter": "4.12.2",
"@koa/cors": "5.0.0",
"@lokalise/node-api": "12.5.0",
"@lokalise/node-api": "12.4.1",
"@octokit/auth-oauth-device": "7.1.1",
"@octokit/plugin-retry": "7.1.1",
"@octokit/rest": "21.0.0",
"@octokit/rest": "20.1.1",
"@open-wc/dev-server-hmr": "0.1.4",
"@rollup/plugin-babel": "6.0.4",
"@rollup/plugin-commonjs": "26.0.1",
"@rollup/plugin-commonjs": "25.0.7",
"@rollup/plugin-json": "6.1.0",
"@rollup/plugin-node-resolve": "15.2.3",
"@rollup/plugin-replace": "5.0.7",
"@rollup/plugin-replace": "5.0.5",
"@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.15",
"@types/chromecast-caf-sender": "1.0.10",
"@types/chromecast-caf-receiver": "6.0.14",
"@types/chromecast-caf-sender": "1.0.9",
"@types/color-name": "1.1.4",
"@types/glob": "8.1.0",
"@types/html-minifier-terser": "7.0.2",
@@ -185,8 +185,8 @@
"@types/tar": "6.1.13",
"@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29",
"@typescript-eslint/eslint-plugin": "7.13.1",
"@typescript-eslint/parser": "7.13.1",
"@typescript-eslint/eslint-plugin": "7.8.0",
"@typescript-eslint/parser": "7.8.0",
"@web/dev-server": "0.1.38",
"@web/dev-server-rollup": "0.4.1",
"babel-loader": "9.1.3",
@@ -198,14 +198,15 @@
"eslint-config-airbnb-typescript": "18.0.0",
"eslint-config-prettier": "9.1.0",
"eslint-import-resolver-webpack": "0.13.8",
"eslint-plugin-disable": "2.0.3",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-lit": "1.14.0",
"eslint-plugin-lit": "1.11.0",
"eslint-plugin-lit-a11y": "4.1.2",
"eslint-plugin-unused-imports": "4.0.0",
"eslint-plugin-unused-imports": "3.2.0",
"eslint-plugin-wc": "2.1.0",
"fancy-log": "2.0.0",
"fs-extra": "11.2.0",
"glob": "10.4.2",
"glob": "10.3.14",
"gulp": "5.0.0",
"gulp-json-transform": "0.5.0",
"gulp-rename": "2.0.0",
@@ -214,7 +215,7 @@
"husky": "9.0.11",
"instant-mocha": "1.5.2",
"jszip": "3.10.1",
"lint-staged": "15.2.7",
"lint-staged": "15.2.2",
"lit-analyzer": "2.0.3",
"lodash.merge": "4.6.2",
"lodash.template": "4.5.0",
@@ -224,27 +225,27 @@
"object-hash": "3.0.0",
"open": "10.1.0",
"pinst": "3.0.0",
"prettier": "3.3.2",
"prettier": "3.2.5",
"rollup": "2.79.1",
"rollup-plugin-string": "3.0.0",
"rollup-plugin-terser": "7.0.2",
"rollup-plugin-visualizer": "5.12.0",
"serve-handler": "6.1.5",
"sinon": "18.0.0",
"sinon": "17.0.2",
"source-map-url": "0.4.1",
"systemjs": "6.15.1",
"tar": "7.4.0",
"tar": "7.1.0",
"terser-webpack-plugin": "5.3.10",
"transform-async-modules-webpack-plugin": "1.1.1",
"ts-lit-plugin": "2.0.2",
"typescript": "5.4.5",
"webpack": "5.92.1",
"webpack": "5.91.0",
"webpack-cli": "5.1.4",
"webpack-dev-server": "5.0.4",
"webpack-manifest-plugin": "5.0.0",
"webpack-stats-plugin": "1.1.3",
"webpackbar": "6.0.1",
"workbox-build": "7.1.1"
"workbox-build": "7.1.0"
},
"_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch",
"resolutions": {
@@ -253,9 +254,8 @@
"lit": "2.8.0",
"clean-css": "5.3.3",
"@lit/reactive-element": "1.6.3",
"@fullcalendar/daygrid": "6.1.11",
"sortablejs@1.15.2": "patch:sortablejs@npm%3A1.15.2#~/.yarn/patches/sortablejs-npm-1.15.2-73347ae85a.patch",
"leaflet-draw@1.0.4": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
},
"packageManager": "yarn@4.3.1"
"packageManager": "yarn@4.2.2"
}

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
version = "20240610.0"
version = "20240501.0"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"

View File

@@ -31,7 +31,6 @@ import {
mdiFormatListBulleted,
mdiFormatListCheckbox,
mdiFormTextbox,
mdiForumOutline,
mdiGauge,
mdiGoogleAssistant,
mdiGoogleCirclesCommunities,
@@ -99,7 +98,7 @@ export const FIXED_DOMAIN_ICONS = {
calendar: mdiCalendar,
climate: mdiThermostat,
configurator: mdiCog,
conversation: mdiForumOutline,
conversation: mdiMicrophoneMessage,
counter: mdiCounter,
date: mdiCalendar,
datetime: mdiCalendarClock,
@@ -236,8 +235,6 @@ export const SENSOR_ENTITIES = [
"weather",
];
export const ASSIST_ENTITIES = ["conversation", "stt", "tts"];
/** Domains that render an input element instead of a text value when displayed in a row.
* Those rows should then not show a cursor pointer when hovered (which would normally
* be the default) unless the element itself enforces it (e.g. a button). Also those elements

View File

@@ -1,6 +1,8 @@
import { getWeekStartByLocale } from "weekstart";
import { FrontendLocaleData, FirstWeekday } from "../../data/translation";
import "../../resources/intl-polyfill";
export const weekdays = [
"sunday",
"monday",

View File

@@ -1,6 +1,7 @@
import { HassConfig } from "home-assistant-js-websocket";
import memoizeOne from "memoize-one";
import { DateFormat, FrontendLocaleData } from "../../data/translation";
import "../../resources/intl-polyfill";
import { resolveTimeZone } from "./resolve-time-zone";
// Tuesday, August 10

View File

@@ -1,6 +1,7 @@
import { HassConfig } from "home-assistant-js-websocket";
import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import "../../resources/intl-polyfill";
import { formatDateNumeric } from "./format_date";
import { formatTime } from "./format_time";
import { resolveTimeZone } from "./resolve-time-zone";

View File

@@ -1,5 +1,6 @@
import { HaDurationData } from "../../components/ha-duration-input";
import { FrontendLocaleData } from "../../data/translation";
import "../../resources/intl-polyfill";
const leftPad = (num: number) => (num < 10 ? `0${num}` : num);

View File

@@ -1,6 +1,7 @@
import { HassConfig } from "home-assistant-js-websocket";
import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import "../../resources/intl-polyfill";
import { resolveTimeZone } from "./resolve-time-zone";
import { useAmPm } from "./use_am_pm";

View File

@@ -1,4 +1,5 @@
import memoizeOne from "memoize-one";
import "../../resources/intl-polyfill";
export const localizeWeekdays = memoizeOne(
(language: string, short: boolean): string[] => {

View File

@@ -1,5 +1,6 @@
import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import "../../resources/intl-polyfill";
import { selectUnit } from "../util/select-unit";
const formatRelTimeMem = memoizeOne(

View File

@@ -108,8 +108,6 @@ export const storage =
subscribe?: boolean;
state?: boolean;
stateOptions?: InternalPropertyDeclaration;
serializer?: (value: any) => any;
deserializer?: (value: any) => any;
}): any =>
(clsElement: ClassElement) => {
const storageName = options.storage || "localStorage";
@@ -143,9 +141,7 @@ export const storage =
const getValue = (): any =>
storageInstance.hasKey(storageKey!)
? options.deserializer
? options.deserializer(storageInstance.getValue(storageKey!))
: storageInstance.getValue(storageKey!)
? storageInstance.getValue(storageKey!)
: initVal;
const setValue = (el: ReactiveElement, value: any) => {
@@ -153,10 +149,7 @@ export const storage =
if (options.state) {
oldValue = getValue();
}
storageInstance.setValue(
storageKey!,
options.serializer ? options.serializer(value) : value
);
storageInstance.setValue(storageKey!, value);
if (options.state) {
el.requestUpdate(clsElement.key, oldValue);
}

View File

@@ -61,11 +61,8 @@ export const applyThemesOnElement = (
const accentColor = themeSettings?.accentColor;
if (darkMode && primaryColor) {
themeRules["app-header-background-color"] = hexBlend(
primaryColor,
"#121212",
8
);
themeRules["app-theme-color"] = hexBlend(primaryColor, "#121212", 8);
themeRules["app-header-background-color"] = themeRules["app-theme-color"];
}
if (primaryColor) {

View File

@@ -1,5 +1,3 @@
export type MediaQueriesListener = () => void;
/**
* Attach a media query. Listener is called right away and when it matches.
* @param mediaQuery media query to match.
@@ -9,7 +7,7 @@ export type MediaQueriesListener = () => void;
export const listenMediaQuery = (
mediaQuery: string,
matchesChanged: (matches: boolean) => void
): MediaQueriesListener => {
) => {
const mql = matchMedia(mediaQuery);
const listener = (e) => matchesChanged(e.matches);
mql.addListener(listener);

View File

@@ -1 +0,0 @@
export const preventDefault = (ev) => ev.preventDefault();

View File

@@ -19,11 +19,28 @@ import { blankBeforeUnit } from "../translations/blank_before_unit";
import { LocalizeFunc } from "../translations/localize";
import { computeDomain } from "./compute_domain";
export const computeStateDisplaySingleEntity = (
localize: LocalizeFunc,
stateObj: HassEntity,
locale: FrontendLocaleData,
config: HassConfig,
entity: EntityRegistryDisplayEntry | undefined,
state?: string
): string =>
computeStateDisplayFromEntityAttributes(
localize,
locale,
config,
entity,
stateObj.entity_id,
stateObj.attributes,
state !== undefined ? state : stateObj.state
);
export const computeStateDisplay = (
localize: LocalizeFunc,
stateObj: HassEntity,
locale: FrontendLocaleData,
sensorNumericDeviceClasses: string[],
config: HassConfig,
entities: HomeAssistant["entities"],
state?: string
@@ -35,7 +52,6 @@ export const computeStateDisplay = (
return computeStateDisplayFromEntityAttributes(
localize,
locale,
sensorNumericDeviceClasses,
config,
entity,
stateObj.entity_id,
@@ -47,7 +63,6 @@ export const computeStateDisplay = (
export const computeStateDisplayFromEntityAttributes = (
localize: LocalizeFunc,
locale: FrontendLocaleData,
sensorNumericDeviceClasses: string[],
config: HassConfig,
entity: EntityRegistryDisplayEntry | undefined,
entityId: string,
@@ -58,15 +73,8 @@ export const computeStateDisplayFromEntityAttributes = (
return localize(`state.default.${state}`);
}
const domain = computeDomain(entityId);
// Entities with a `unit_of_measurement` or `state_class` are numeric values and should use `formatNumber`
if (
isNumericFromAttributes(
attributes,
domain === "sensor" ? sensorNumericDeviceClasses : []
)
) {
if (isNumericFromAttributes(attributes)) {
// state is duration
if (
attributes.device_class === "duration" &&
@@ -112,6 +120,8 @@ export const computeStateDisplayFromEntityAttributes = (
return value;
}
const domain = computeDomain(entityId);
if (domain === "datetime") {
const time = new Date(state);
return formatDateTime(time, locale, config);

View File

@@ -12,10 +12,11 @@ export const formatLanguageCode = (
}
};
const formatLanguageCodeMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DisplayNames(locale.language, {
type: "language",
fallback: "code",
})
const formatLanguageCodeMem = memoizeOne((locale: FrontendLocaleData) =>
Intl && "DisplayNames" in Intl
? new Intl.DisplayNames(locale.language, {
type: "language",
fallback: "code",
})
: undefined
);

View File

@@ -11,7 +11,6 @@ declare global {
export interface NavigateOptions {
replace?: boolean;
data?: any;
}
export const navigate = (path: string, options?: NavigateOptions) => {
@@ -25,7 +24,7 @@ export const navigate = (path: string, options?: NavigateOptions) => {
if (__DEMO__) {
if (replace) {
mainWindow.history.replaceState(
mainWindow.history.state?.root ? { root: true } : options?.data ?? null,
mainWindow.history.state?.root ? { root: true } : null,
"",
`${mainWindow.location.pathname}#${path}`
);
@@ -34,12 +33,12 @@ export const navigate = (path: string, options?: NavigateOptions) => {
}
} else if (replace) {
mainWindow.history.replaceState(
mainWindow.history.state?.root ? { root: true } : options?.data ?? null,
mainWindow.history.state?.root ? { root: true } : null,
"",
path
);
} else {
mainWindow.history.pushState(options?.data ?? null, "", path);
mainWindow.history.pushState(null, "", path);
}
fireEvent(mainWindow, "location-changed", {
replace,

View File

@@ -14,12 +14,8 @@ export const isNumericState = (stateObj: HassEntity): boolean =>
isNumericFromAttributes(stateObj.attributes);
export const isNumericFromAttributes = (
attributes: HassEntityAttributeBase,
numericDeviceClasses?: string[]
): boolean =>
!!attributes.unit_of_measurement ||
!!attributes.state_class ||
(numericDeviceClasses || []).includes(attributes.device_class || "");
attributes: HassEntityAttributeBase
): boolean => !!attributes.unit_of_measurement || !!attributes.state_class;
export const numberFormatToLocale = (
localeOptions: FrontendLocaleData
@@ -63,18 +59,30 @@ export const formatNumber = (
if (
localeOptions?.number_format !== NumberFormat.none &&
!Number.isNaN(Number(num))
!Number.isNaN(Number(num)) &&
Intl
) {
return new Intl.NumberFormat(
locale,
getDefaultFormatOptions(num, options)
).format(Number(num));
try {
return new Intl.NumberFormat(
locale,
getDefaultFormatOptions(num, options)
).format(Number(num));
} catch (err: any) {
// Don't fail when using "TEST" language
// eslint-disable-next-line no-console
console.error(err);
return new Intl.NumberFormat(
undefined,
getDefaultFormatOptions(num, options)
).format(Number(num));
}
}
if (
!Number.isNaN(Number(num)) &&
num !== "" &&
localeOptions?.number_format === NumberFormat.none
localeOptions?.number_format === NumberFormat.none &&
Intl
) {
// If NumberFormat is none, use en-US format without grouping.
return new Intl.NumberFormat(

View File

@@ -1,4 +1,5 @@
import memoizeOne from "memoize-one";
import "../../resources/intl-polyfill";
import { FrontendLocaleData } from "../../data/translation";
export const formatListWithAnds = (

View File

@@ -21,8 +21,7 @@ export const computeFormatFunctions = async (
localize: LocalizeFunc,
locale: FrontendLocaleData,
config: HassConfig,
entities: HomeAssistant["entities"],
sensorNumericDeviceClasses: string[]
entities: HomeAssistant["entities"]
): Promise<{
formatEntityState: FormatEntityStateFunc;
formatEntityAttributeValue: FormatEntityAttributeValueFunc;
@@ -36,15 +35,7 @@ export const computeFormatFunctions = async (
return {
formatEntityState: (stateObj, state) =>
computeStateDisplay(
localize,
stateObj,
locale,
sensorNumericDeviceClasses,
config,
entities,
state
),
computeStateDisplay(localize, stateObj, locale, config, entities, state),
formatEntityAttributeValue: (stateObj, attribute, value) =>
computeAttributeValueDisplay(
localize,

View File

@@ -1,6 +1,6 @@
import type { IntlMessageFormat } from "intl-messageformat";
import IntlMessageFormat from "intl-messageformat";
import type { HTMLTemplateResult } from "lit";
import { polyfillLocaleData } from "../../resources/polyfills/locale-data-polyfill";
import { polyfillLocaleData } from "../../resources/locale-data-polyfill";
import { Resources, TranslationDict } from "../../types";
import { fireEvent } from "../dom/fire_event";
@@ -89,8 +89,9 @@ export const computeLocalize = async <Keys extends string = LocalizeKeys>(
resources: Resources,
formats?: FormatsType
): Promise<LocalizeFunc<Keys>> => {
const { IntlMessageFormat } = await import("intl-messageformat");
await polyfillLocaleData(language);
await import("../../resources/intl-polyfill").then(() =>
polyfillLocaleData(language)
);
// Every time any of the parameters change, invalidate the strings cache.
cache._localizationCache = {};

View File

@@ -313,38 +313,31 @@ export class HaChartBase extends LitElement {
`;
}
private _loading = false;
private async _setupChart() {
if (this._loading) return;
const ctx: CanvasRenderingContext2D = this.renderRoot
.querySelector("canvas")!
.getContext("2d")!;
this._loading = true;
try {
const ChartConstructor = (await import("../../resources/chartjs")).Chart;
const computedStyles = getComputedStyle(this);
const ChartConstructor = (await import("../../resources/chartjs")).Chart;
ChartConstructor.defaults.borderColor =
computedStyles.getPropertyValue("--divider-color");
ChartConstructor.defaults.color = computedStyles.getPropertyValue(
"--secondary-text-color"
);
ChartConstructor.defaults.font.family =
computedStyles.getPropertyValue("--mdc-typography-body1-font-family") ||
computedStyles.getPropertyValue("--mdc-typography-font-family") ||
"Roboto, Noto, sans-serif";
const computedStyles = getComputedStyle(this);
this.chart = new ChartConstructor(ctx, {
type: this.chartType,
data: this.data,
options: this._createOptions(),
plugins: this._createPlugins(),
});
} finally {
this._loading = false;
}
ChartConstructor.defaults.borderColor =
computedStyles.getPropertyValue("--divider-color");
ChartConstructor.defaults.color = computedStyles.getPropertyValue(
"--secondary-text-color"
);
ChartConstructor.defaults.font.family =
computedStyles.getPropertyValue("--mdc-typography-body1-font-family") ||
computedStyles.getPropertyValue("--mdc-typography-font-family") ||
"Roboto, Noto, sans-serif";
this.chart = new ChartConstructor(ctx, {
type: this.chartType,
data: this.data,
options: this._createOptions(),
plugins: this._createPlugins(),
});
}
private _createOptions() {

View File

@@ -1,3 +1,4 @@
import "element-internals-polyfill";
import { MdAssistChip } from "@material/web/chips/assist-chip";
import { css, html } from "lit";
import { customElement, property } from "lit/decorators";

View File

@@ -1,3 +1,4 @@
import "element-internals-polyfill";
import { MdChipSet } from "@material/web/chips/chip-set";
import { customElement } from "lit/decorators";

View File

@@ -1,3 +1,4 @@
import "element-internals-polyfill";
import { MdFilterChip } from "@material/web/chips/filter-chip";
import { css, html } from "lit";
import { customElement, property } from "lit/decorators";

View File

@@ -1,3 +1,4 @@
import "element-internals-polyfill";
import { MdInputChip } from "@material/web/chips/input-chip";
import { css } from "lit";
import { customElement } from "lit/decorators";

View File

@@ -730,28 +730,6 @@ export class HaDataTable extends LitElement {
fireEvent(this, "collapsed-changed", { value: this._collapsedGroups });
};
public expandAllGroups() {
this._collapsedGroups = [];
fireEvent(this, "collapsed-changed", { value: this._collapsedGroups });
}
public collapseAllGroups() {
if (
!this.groupColumn ||
!this.data.some((item) => item[this.groupColumn!])
) {
return;
}
const grouped = groupBy(this.data, (item) => item[this.groupColumn!]);
if (grouped.undefined) {
// undefined is a reserved group name
grouped[UNDEFINED_GROUP_KEY] = grouped.undefined;
delete grouped.undefined;
}
this._collapsedGroups = Object.keys(grouped);
fireEvent(this, "collapsed-changed", { value: this._collapsedGroups });
}
static get styles(): CSSResultGroup {
return [
haStyleScrollbar,

View File

@@ -405,9 +405,9 @@ export class HaEntityPicker extends LitElement {
this._opened = ev.detail.value;
}
private _valueChanged(ev: ValueChangedEvent<string | undefined>) {
private _valueChanged(ev: ValueChangedEvent<string>) {
ev.stopPropagation();
const newValue = ev.detail.value?.trim();
const newValue = ev.detail.value;
if (newValue && newValue.startsWith(CREATE_ID)) {
const domain = newValue.substring(CREATE_ID.length);
@@ -427,13 +427,13 @@ export class HaEntityPicker extends LitElement {
private _filterChanged(ev: CustomEvent): void {
const target = ev.target as HaComboBox;
const filterString = ev.detail.value.trim().toLowerCase();
const filterString = ev.detail.value.toLowerCase();
target.filteredItems = filterString.length
? fuzzyFilterSort<HassEntityWithCachedName>(filterString, this._states)
: this._states;
}
private _setValue(value: string | undefined) {
private _setValue(value: string) {
this.value = value;
setTimeout(() => {
fireEvent(this, "value-changed", { value });

View File

@@ -1,3 +1,4 @@
import "element-internals-polyfill";
import { MdCircularProgress } from "@material/web/progress/circular-progress";
import { PropertyValues, css } from "lit";
import { customElement, property } from "lit/decorators";

View File

@@ -47,8 +47,6 @@ export class HaCodeEditor extends ReactiveElement {
@property({ type: Boolean }) public readOnly = false;
@property({ type: Boolean }) public linewrap = false;
@property({ type: Boolean, attribute: "autocomplete-entities" })
public autocompleteEntities = false;
@@ -136,13 +134,6 @@ export class HaCodeEditor extends ReactiveElement {
),
});
}
if (changedProps.has("linewrap")) {
transactions.push({
effects: this._loadedCodeMirror!.linewrapCompartment!.reconfigure(
this.linewrap ? this._loadedCodeMirror!.EditorView.lineWrapping : []
),
});
}
if (changedProps.has("_value") && this._value !== this.value) {
transactions.push({
changes: {
@@ -190,9 +181,6 @@ export class HaCodeEditor extends ReactiveElement {
this._loadedCodeMirror.readonlyCompartment.of(
this._loadedCodeMirror.EditorView.editable.of(!this.readOnly)
),
this._loadedCodeMirror.linewrapCompartment.of(
this.linewrap ? this._loadedCodeMirror.EditorView.lineWrapping : []
),
this._loadedCodeMirror.EditorView.updateListener.of(this._onUpdate),
];

View File

@@ -4,6 +4,7 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event";
import { stopPropagation } from "../common/dom/stop_propagation";
import { caseInsensitiveStringCompare } from "../common/string/compare";
import "../resources/intl-polyfill";
import "./ha-list-item";
import "./ha-select";
import type { HaSelect } from "./ha-select";
@@ -281,10 +282,14 @@ export class HaCountryPicker extends LitElement {
private _getOptions = memoizeOne(
(language?: string, countries?: string[]) => {
let options: { label: string; value: string }[] = [];
const countryDisplayNames = new Intl.DisplayNames(language, {
type: "region",
fallback: "code",
});
const countryDisplayNames =
Intl && "DisplayNames" in Intl
? new Intl.DisplayNames(language, {
type: "region",
fallback: "code",
})
: undefined;
if (countries) {
options = countries.map((country) => ({
value: country,

View File

@@ -4,6 +4,7 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event";
import { stopPropagation } from "../common/dom/stop_propagation";
import { caseInsensitiveStringCompare } from "../common/string/compare";
import "../resources/intl-polyfill";
import "./ha-list-item";
import "./ha-select";
import type { HaSelect } from "./ha-select";
@@ -169,9 +170,12 @@ const CURRENCIES = [
];
const curSymbol = (currency: string, locale?: string) =>
new Intl.NumberFormat(locale, { style: "currency", currency })
.formatToParts(1)
.find((x) => x.type === "currency")?.value;
Intl && "NumberFormat" in Intl
? new Intl.NumberFormat(locale, { style: "currency", currency })
.formatToParts(1)
.find((x) => x.type === "currency")?.value
: currency;
@customElement("ha-currency-picker")
export class HaCurrencyPicker extends LitElement {
@property() public language = "en";
@@ -185,10 +189,13 @@ export class HaCurrencyPicker extends LitElement {
@property({ type: Boolean, reflect: true }) public disabled = false;
private _getOptions = memoizeOne((language?: string) => {
const currencyDisplayNames = new Intl.DisplayNames(language, {
type: "currency",
fallback: "code",
});
const currencyDisplayNames =
Intl && "DisplayNames" in Intl
? new Intl.DisplayNames(language, {
type: "currency",
fallback: "code",
})
: undefined;
const options = CURRENCIES.map((currency) => ({
value: currency,
label: `${

View File

@@ -21,8 +21,6 @@ export class HaExpansionPanel extends LitElement {
@property({ type: Boolean, reflect: true }) leftChevron = false;
@property({ type: Boolean, reflect: true }) noCollapse = false;
@property() header?: string;
@property() secondary?: string;
@@ -36,17 +34,16 @@ export class HaExpansionPanel extends LitElement {
<div class="top ${classMap({ expanded: this.expanded })}">
<div
id="summary"
class=${classMap({ noCollapse: this.noCollapse })}
@click=${this._toggleContainer}
@keydown=${this._toggleContainer}
@focus=${this._focusChanged}
@blur=${this._focusChanged}
role="button"
tabindex=${this.noCollapse ? -1 : 0}
tabindex="0"
aria-expanded=${this.expanded}
aria-controls="sect1"
>
${this.leftChevron && !this.noCollapse
${this.leftChevron
? html`
<ha-svg-icon
.path=${mdiChevronDown}
@@ -60,7 +57,7 @@ export class HaExpansionPanel extends LitElement {
<slot class="secondary" name="secondary">${this.secondary}</slot>
</div>
</slot>
${!this.leftChevron && !this.noCollapse
${!this.leftChevron
? html`
<ha-svg-icon
.path=${mdiChevronDown}
@@ -109,9 +106,6 @@ export class HaExpansionPanel extends LitElement {
return;
}
ev.preventDefault();
if (this.noCollapse) {
return;
}
const newExpanded = !this.expanded;
fireEvent(this, "expanded-will-change", { expanded: newExpanded });
this._container.style.overflow = "hidden";
@@ -136,9 +130,6 @@ export class HaExpansionPanel extends LitElement {
}
private _focusChanged(ev) {
if (this.noCollapse) {
return;
}
this.shadowRoot!.querySelector(".top")!.classList.toggle(
"focused",
ev.type === "focus"
@@ -200,9 +191,6 @@ export class HaExpansionPanel extends LitElement {
font-weight: 500;
outline: none;
}
#summary.noCollapse {
cursor: default;
}
.summary-icon.expanded {
transform: rotate(180deg);

View File

@@ -1,14 +1,7 @@
import { SelectedDetail } from "@material/mwc-list";
import "@material/mwc-menu/mwc-menu-surface";
import { mdiFilterVariantRemove } from "@mdi/js";
import {
css,
CSSResultGroup,
html,
LitElement,
nothing,
PropertyValues,
} from "lit";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import { Blueprints, fetchBlueprints } from "../data/blueprint";
@@ -32,16 +25,6 @@ export class HaFilterBlueprints extends LitElement {
@state() private _blueprints?: Blueprints;
public willUpdate(properties: PropertyValues) {
super.willUpdate(properties);
if (!this.hasUpdated) {
if (this.value?.length) {
this._findRelated();
}
}
}
protected render() {
return html`
<ha-expansion-panel
@@ -113,6 +96,7 @@ export class HaFilterBlueprints extends LitElement {
ev: CustomEvent<SelectedDetail<Set<number>>>
) {
const blueprints = this._blueprints!;
const relatedPromises: Promise<RelatedResult>[] = [];
if (!ev.detail.index.size) {
fireEvent(this, "data-table-filter-changed", {
@@ -128,33 +112,13 @@ export class HaFilterBlueprints extends LitElement {
for (const index of ev.detail.index) {
const blueprintId = Object.keys(blueprints)[index];
value.push(blueprintId);
}
this.value = value;
this._findRelated();
}
private async _findRelated() {
if (!this.value?.length) {
fireEvent(this, "data-table-filter-changed", {
value: [],
items: undefined,
});
this.value = [];
return;
}
const relatedPromises: Promise<RelatedResult>[] = [];
for (const blueprintId of this.value) {
if (this.type) {
relatedPromises.push(
findRelated(this.hass, `${this.type}_blueprint`, blueprintId)
);
}
}
this.value = value;
const results = await Promise.all(relatedPromises);
const items: Set<string> = new Set();
for (const result of results) {
@@ -164,7 +128,7 @@ export class HaFilterBlueprints extends LitElement {
}
fireEvent(this, "data-table-filter-changed", {
value: this.value,
value,
items: this.type ? items : undefined,
});
}

View File

@@ -41,9 +41,6 @@ export class HaFilterDevices extends LitElement {
if (!this.hasUpdated) {
loadVirtualizer();
if (this.value?.length) {
this._findRelated();
}
}
}

View File

@@ -87,20 +87,9 @@ export class HaFilterDomains extends LitElement {
Object.keys(states).forEach((entityId) => {
domains.add(computeDomain(entityId));
});
return Array.from(domains.values())
.map((domain) => ({
domain,
name: domainToName(this.hass.localize, domain),
}))
.filter(
(entry) =>
!filter ||
entry.domain.toLowerCase().includes(filter) ||
entry.name.toLowerCase().includes(filter)
)
.sort((a, b) => stringCompare(a.name, b.name, this.hass.locale.language))
.map((entry) => entry.domain);
return Array.from(domains)
.filter((domain) => !filter || domain.toLowerCase().includes(filter))
.sort((a, b) => stringCompare(a, b, this.hass.locale.language));
});
protected updated(changed) {

View File

@@ -42,9 +42,6 @@ export class HaFilterEntities extends LitElement {
if (!this.hasUpdated) {
loadVirtualizer();
if (this.value?.length) {
this._findRelated();
}
}
}
@@ -189,12 +186,15 @@ export class HaFilterEntities extends LitElement {
return;
}
const value: string[] = [];
for (const entityId of this.value) {
value.push(entityId);
if (this.type) {
relatedPromises.push(findRelated(this.hass, "entity", entityId));
}
}
this.value = value;
const results = await Promise.all(relatedPromises);
const items: Set<string> = new Set();
for (const result of results) {
@@ -204,7 +204,7 @@ export class HaFilterEntities extends LitElement {
}
fireEvent(this, "data-table-filter-changed", {
value: this.value,
value,
items: this.type ? items : undefined,
});
}

View File

@@ -1,14 +1,7 @@
import "@material/mwc-menu/mwc-menu-surface";
import { mdiFilterVariantRemove, mdiTextureBox } from "@mdi/js";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import {
CSSResultGroup,
LitElement,
PropertyValues,
css,
html,
nothing,
} from "lit";
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { repeat } from "lit/directives/repeat";
@@ -49,16 +42,6 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
@state() private _floors?: FloorRegistryEntry[];
public willUpdate(properties: PropertyValues) {
super.willUpdate(properties);
if (!this.hasUpdated) {
if (this.value?.floors?.length || this.value?.areas?.length) {
this._findRelated();
}
}
}
protected render() {
const areas = this._areas(this.hass.areas, this._floors);
@@ -207,10 +190,6 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
}
}
protected firstUpdated() {
this._findRelated();
}
private _expandedWillChange(ev) {
this._shouldRender = ev.detail.expanded;
}

View File

@@ -2,7 +2,7 @@ import type { Selector } from "../../data/selector";
import type { HaFormSchema } from "./types";
export const computeInitialHaFormData = (
schema: HaFormSchema[] | readonly HaFormSchema[]
schema: HaFormSchema[]
): Record<string, any> => {
const data = {};
schema.forEach((field) => {
@@ -36,8 +36,6 @@ export const computeInitialHaFormData = (
minutes: 0,
seconds: 0,
};
} else if (field.type === "expandable") {
data[field.name] = computeInitialHaFormData(field.schema);
} else if ("selector" in field) {
const selector: Selector = field.selector;

View File

@@ -1,233 +0,0 @@
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "./ha-icon-button";
import "../panels/lovelace/editor/card-editor/ha-grid-layout-slider";
import { mdiRestore } from "@mdi/js";
import { styleMap } from "lit/directives/style-map";
import { fireEvent } from "../common/dom/fire_event";
import { HomeAssistant } from "../types";
type GridSizeValue = {
rows?: number;
columns?: number;
};
@customElement("ha-grid-size-picker")
export class HaGridSizeEditor extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public value?: GridSizeValue;
@property({ attribute: false }) public rows = 6;
@property({ attribute: false }) public columns = 4;
@property({ attribute: false }) public rowMin?: number;
@property({ attribute: false }) public rowMax?: number;
@property({ attribute: false }) public columnMin?: number;
@property({ attribute: false }) public columnMax?: number;
@property({ attribute: false }) public isDefault?: boolean;
@state() public _localValue?: GridSizeValue = undefined;
protected willUpdate(changedProperties) {
if (changedProperties.has("value")) {
this._localValue = this.value;
}
}
protected render() {
return html`
<div class="grid">
<ha-grid-layout-slider
aria-label=${this.hass.localize(
"ui.components.grid-size-picker.columns"
)}
id="columns"
.min=${this.columnMin ?? 1}
.max=${this.columnMax ?? this.columns}
.range=${this.columns}
.value=${this.value?.columns}
@value-changed=${this._valueChanged}
@slider-moved=${this._sliderMoved}
></ha-grid-layout-slider>
<ha-grid-layout-slider
aria-label=${this.hass.localize(
"ui.components.grid-size-picker.rows"
)}
id="rows"
.min=${this.rowMin ?? 1}
.max=${this.rowMax ?? this.rows}
.range=${this.rows}
vertical
.value=${this.value?.rows}
@value-changed=${this._valueChanged}
@slider-moved=${this._sliderMoved}
></ha-grid-layout-slider>
${!this.isDefault
? html`
<ha-icon-button
@click=${this._reset}
class="reset"
.path=${mdiRestore}
label=${this.hass.localize(
"ui.components.grid-size-picker.reset_default"
)}
title=${this.hass.localize(
"ui.components.grid-size-picker.reset_default"
)}
>
</ha-icon-button>
`
: nothing}
<div
class="preview"
style=${styleMap({
"--total-rows": this.rows,
"--total-columns": this.columns,
"--rows": this._localValue?.rows,
"--columns": this._localValue?.columns,
})}
>
<div>
${Array(this.rows * this.columns)
.fill(0)
.map((_, index) => {
const row = Math.floor(index / this.columns) + 1;
const column = (index % this.columns) + 1;
const disabled =
(this.rowMin !== undefined && row < this.rowMin) ||
(this.rowMax !== undefined && row > this.rowMax) ||
(this.columnMin !== undefined && column < this.columnMin) ||
(this.columnMax !== undefined && column > this.columnMax);
return html`
<div
class="cell"
data-row=${row}
data-column=${column}
?disabled=${disabled}
@click=${this._cellClick}
></div>
`;
})}
</div>
<div class="selected">
<div class="cell"></div>
</div>
</div>
</div>
`;
}
_cellClick(ev) {
const cell = ev.currentTarget as HTMLElement;
if (cell.getAttribute("disabled") !== null) return;
const rows = Number(cell.getAttribute("data-row"));
const columns = Number(cell.getAttribute("data-column"));
fireEvent(this, "value-changed", {
value: { rows, columns },
});
}
private _valueChanged(ev) {
ev.stopPropagation();
const key = ev.currentTarget.id;
const newValue = {
...this.value,
[key]: ev.detail.value,
};
fireEvent(this, "value-changed", { value: newValue });
}
private _reset(ev) {
ev.stopPropagation();
fireEvent(this, "value-changed", {
value: {
rows: undefined,
columns: undefined,
},
});
}
private _sliderMoved(ev) {
ev.stopPropagation();
const key = ev.currentTarget.id;
const value = ev.detail.value;
if (value === undefined) return;
this._localValue = {
...this.value,
[key]: ev.detail.value,
};
}
static styles = [
css`
.grid {
display: grid;
grid-template-areas:
"reset column-slider"
"row-slider preview";
grid-template-rows: auto 1fr;
grid-template-columns: auto 1fr;
gap: 8px;
}
#columns {
grid-area: column-slider;
}
#rows {
grid-area: row-slider;
}
.reset {
grid-area: reset;
}
.preview {
position: relative;
grid-area: preview;
aspect-ratio: 1 / 1;
}
.preview > div {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: grid;
grid-template-columns: repeat(var(--total-columns), 1fr);
grid-template-rows: repeat(var(--total-rows), 1fr);
gap: 4px;
}
.preview .cell {
background-color: var(--disabled-color);
grid-column: span 1;
grid-row: span 1;
border-radius: 4px;
opacity: 0.2;
cursor: pointer;
}
.preview .cell[disabled] {
opacity: 0.05;
cursor: initial;
}
.selected {
pointer-events: none;
}
.selected .cell {
background-color: var(--primary-color);
grid-column: 1 / span var(--columns, 0);
grid-row: 1 / span var(--rows, 0);
opacity: 0.5;
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"ha-grid-size-picker": HaGridSizeEditor;
}
}

View File

@@ -1,3 +1,4 @@
import "@material/mwc-list/mwc-list-item";
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
import {
ComboBoxDataProviderCallback,
@@ -10,7 +11,6 @@ import { fireEvent } from "../common/dom/fire_event";
import { customIcons } from "../data/custom_icons";
import { HomeAssistant, ValueChangedEvent } from "../types";
import "./ha-combo-box";
import "./ha-list-item";
import "./ha-icon";
type IconItem = {
@@ -67,10 +67,10 @@ const loadCustomIconItems = async (iconsetPrefix: string) => {
};
const rowRenderer: ComboBoxLitRenderer<IconItem | RankedIcon> = (item) =>
html`<ha-list-item graphic="avatar">
html`<mwc-list-item graphic="avatar">
<ha-icon .icon=${item.icon} slot="graphic"></ha-icon>
${item.icon}
</ha-list-item>`;
</mwc-list-item>`;
@customElement("ha-icon-picker")
export class HaIconPicker extends LitElement {
@@ -198,7 +198,8 @@ export class HaIconPicker extends LitElement {
static get styles() {
return css`
*[slot="icon"] {
ha-icon,
ha-svg-icon {
color: var(--primary-text-color);
position: relative;
bottom: 2px;

View File

@@ -6,6 +6,7 @@ import { stopPropagation } from "../common/dom/stop_propagation";
import { formatLanguageCode } from "../common/language/format_language";
import { caseInsensitiveStringCompare } from "../common/string/compare";
import { FrontendLocaleData } from "../data/translation";
import "../resources/intl-polyfill";
import { translationMetadata } from "../resources/translations-metadata";
import { HomeAssistant } from "../types";
import "./ha-list-item";

View File

@@ -1,6 +1,7 @@
import { customElement } from "lit/decorators";
import "element-internals-polyfill";
import { MdListItem } from "@material/web/list/list-item";
import { css } from "lit";
import { customElement } from "lit/decorators";
@customElement("ha-list-item-new")
export class HaListItemNew extends MdListItem {

View File

@@ -100,7 +100,6 @@ export class HaListItem extends ListItemBase {
span.material-icons:first-of-type,
span.material-icons:last-of-type {
direction: rtl !important;
--direction: rtl;
}
`
: css``,

View File

@@ -1,6 +1,7 @@
import { customElement } from "lit/decorators";
import "element-internals-polyfill";
import { MdList } from "@material/web/list/list";
import { css } from "lit";
import { customElement } from "lit/decorators";
@customElement("ha-list-new")
export class HaListNew extends MdList {

View File

@@ -1,4 +1,5 @@
import { MdMenuItem } from "@material/web/menu/menu-item";
import "element-internals-polyfill";
import { css } from "lit";
import { customElement } from "lit/decorators";

View File

@@ -1,6 +1,7 @@
import { MdMenu } from "@material/web/menu/menu";
import { css } from "lit";
import { customElement } from "lit/decorators";
import "element-internals-polyfill";
import { css } from "lit";
import { MdMenu } from "@material/web/menu/menu";
@customElement("ha-menu")
export class HaMenu extends MdMenu {

View File

@@ -1,6 +1,7 @@
import { MdOutlinedButton } from "@material/web/button/outlined-button";
import { css } from "lit";
import { customElement } from "lit/decorators";
import "element-internals-polyfill";
import { MdOutlinedButton } from "@material/web/button/outlined-button";
@customElement("ha-outlined-button")
export class HaOutlinedButton extends MdOutlinedButton {

View File

@@ -1,4 +1,5 @@
import { MdOutlinedField } from "@material/web/field/outlined-field";
import "element-internals-polyfill";
import { css } from "lit";
import { customElement } from "lit/decorators";
import { literal } from "lit/static-html";

View File

@@ -1,6 +1,7 @@
import { MdOutlinedIconButton } from "@material/web/iconbutton/outlined-icon-button";
import { css } from "lit";
import { customElement } from "lit/decorators";
import "element-internals-polyfill";
import { MdOutlinedIconButton } from "@material/web/iconbutton/outlined-icon-button";
@customElement("ha-outlined-icon-button")
export class HaOutlinedIconButton extends MdOutlinedIconButton {

View File

@@ -1,4 +1,5 @@
import { MdOutlinedTextField } from "@material/web/textfield/outlined-text-field";
import "element-internals-polyfill";
import { css } from "lit";
import { customElement } from "lit/decorators";
import { literal } from "lit/static-html";

View File

@@ -2,7 +2,6 @@ import { mdiImagePlus } from "@mdi/js";
import { LitElement, TemplateResult, css, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import { haStyle } from "../resources/styles";
import { createImage, generateImageThumbnailUrl } from "../data/image_upload";
import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
import {
@@ -32,8 +31,6 @@ export class HaPictureUpload extends LitElement {
@property({ attribute: false }) public cropOptions?: CropOptions;
@property({ type: Boolean }) public original = false;
@property({ type: Number }) public size = 512;
@state() private _uploading = false;
@@ -63,15 +60,13 @@ export class HaPictureUpload extends LitElement {
alt=${this.currentImageAltText ||
this.hass.localize("ui.components.picture-upload.current_image_alt")}
/>
<div>
<ha-button
@click=${this._handleChangeClick}
.label=${this.hass.localize(
"ui.components.picture-upload.change_picture"
)}
>
</ha-button>
</div>
<ha-button
@click=${this._handleChangeClick}
.label=${this.hass.localize(
"ui.components.picture-upload.change_picture"
)}
>
</ha-button>
</div>
</div>`;
}
@@ -127,11 +122,7 @@ export class HaPictureUpload extends LitElement {
this._uploading = true;
try {
const media = await createImage(this.hass, file);
this.value = generateImageThumbnailUrl(
media.id,
this.size,
this.original
);
this.value = generateImageThumbnailUrl(media.id, this.size);
fireEvent(this, "change");
} catch (err: any) {
showAlertDialog(this, {
@@ -143,35 +134,32 @@ export class HaPictureUpload extends LitElement {
}
static get styles() {
return [
haStyle,
css`
:host {
display: block;
height: 240px;
}
ha-file-upload {
height: 100%;
}
.center-vertical {
display: flex;
align-items: center;
height: 100%;
}
.value {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
img {
max-width: 100%;
max-height: 200px;
margin-bottom: 4px;
border-radius: var(--file-upload-image-border-radius);
}
`,
];
return css`
:host {
display: block;
height: 240px;
}
ha-file-upload {
height: 100%;
}
.center-vertical {
display: flex;
align-items: center;
height: 100%;
}
.value {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
img {
max-width: 100%;
max-height: 200px;
margin-bottom: 4px;
border-radius: var(--file-upload-image-border-radius);
}
`;
}
}

View File

@@ -1,5 +1,6 @@
import { AttachableController } from "@material/web/internal/controller/attachable-controller";
import { MdRipple } from "@material/web/ripple/ripple";
import "element-internals-polyfill";
import { css } from "lit";
import { customElement } from "lit/decorators";

View File

@@ -1,145 +0,0 @@
import { css, CSSResultGroup, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { ImageSelector } from "../../data/selector";
import { HomeAssistant } from "../../types";
import "../ha-icon-button";
import "../ha-textarea";
import "../ha-textfield";
import "../ha-picture-upload";
import "../ha-radio";
import type { HaPictureUpload } from "../ha-picture-upload";
import { URL_PREFIX } from "../../data/image_upload";
@customElement("ha-selector-image")
export class HaImageSelector extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public value?: any;
@property() public name?: string;
@property() public label?: string;
@property() public placeholder?: string;
@property() public helper?: string;
@property({ attribute: false }) public selector!: ImageSelector;
@property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public required = true;
@state() private showUpload = false;
protected firstUpdated(changedProps): void {
super.firstUpdated(changedProps);
if (!this.value || this.value.startsWith(URL_PREFIX)) {
this.showUpload = true;
}
}
protected render() {
return html`
<div>
<label>
${this.hass.localize("ui.components.selectors.image.select_image")}
<ha-formfield
.label=${this.hass.localize("ui.components.selectors.image.upload")}
>
<ha-radio
name="mode"
value="upload"
.checked=${this.showUpload}
@change=${this._radioGroupPicked}
></ha-radio>
</ha-formfield>
<ha-formfield
.label=${this.hass.localize("ui.components.selectors.image.url")}
>
<ha-radio
name="mode"
value="url"
.checked=${!this.showUpload}
@change=${this._radioGroupPicked}
></ha-radio>
</ha-formfield>
</label>
${!this.showUpload
? html`
<ha-textfield
.name=${this.name}
.value=${this.value || ""}
.placeholder=${this.placeholder || ""}
.helper=${this.helper}
helperPersistent
.disabled=${this.disabled}
@input=${this._handleChange}
.label=${this.label || ""}
.required=${this.required}
></ha-textfield>
`
: html`
<ha-picture-upload
.hass=${this.hass}
.value=${this.value?.startsWith(URL_PREFIX) ? this.value : null}
.original=${this.selector.image?.original}
.cropOptions=${this.selector.image?.crop}
@change=${this._pictureChanged}
></ha-picture-upload>
`}
</div>
`;
}
private _radioGroupPicked(ev): void {
this.showUpload = ev.target.value === "upload";
}
private _pictureChanged(ev) {
const value = (ev.target as HaPictureUpload).value;
fireEvent(this, "value-changed", { value: value ?? undefined });
}
private _handleChange(ev) {
let value = ev.target.value;
if (this.value === value) {
return;
}
if (value === "" && !this.required) {
value = undefined;
}
fireEvent(this, "value-changed", { value });
}
static get styles(): CSSResultGroup {
return css`
:host {
display: block;
position: relative;
}
div {
display: flex;
flex-direction: column;
}
label {
display: flex;
flex-direction: column;
}
ha-textarea,
ha-textfield {
width: 100%;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-selector-image": HaImageSelector;
}
}

View File

@@ -278,14 +278,6 @@ export class HaSelectSelector extends LitElement {
private _valueChanged(ev) {
ev.stopPropagation();
if (ev.detail?.index === -1 && this.value !== undefined) {
fireEvent(this, "value-changed", {
value: undefined,
});
return;
}
const value = ev.detail?.value || ev.target.value;
if (this.disabled || value === undefined || value === (this.value ?? "")) {
return;

View File

@@ -64,12 +64,6 @@ const SELECTOR_SCHEMAS = {
selector: { boolean: {} },
},
] as const,
floor: [
{
name: "multiple",
selector: { boolean: {} },
},
] as const,
icon: [] as const,
location: [] as const,
media: [] as const,

View File

@@ -32,7 +32,6 @@ export class HaTemplateSelector extends LitElement {
autocomplete-icons
@value-changed=${this._handleChange}
dir="ltr"
linewrap
></ha-code-editor>
${this.helper
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`

View File

@@ -32,7 +32,6 @@ const LOAD_ELEMENTS = {
file: () => import("./ha-selector-file"),
floor: () => import("./ha-selector-floor"),
label: () => import("./ha-selector-label"),
image: () => import("./ha-selector-image"),
language: () => import("./ha-selector-language"),
navigation: () => import("./ha-selector-navigation"),
number: () => import("./ha-selector-number"),

View File

@@ -327,7 +327,6 @@ class HaSidebar extends SubscribeMixin(LitElement) {
for (const entityId of Object.keys(this.hass.states)) {
if (
entityId.startsWith("update.") &&
!this.hass.entities[entityId]?.hidden &&
updateCanInstall(this.hass.states[entityId] as UpdateEntity)
) {
updateCount++;

View File

@@ -1,6 +1,7 @@
import { customElement } from "lit/decorators";
import "element-internals-polyfill";
import { MdSlider } from "@material/web/slider/slider";
import { css } from "lit";
import { customElement } from "lit/decorators";
import { mainWindow } from "../common/dom/get_main_window";
@customElement("ha-slider")

View File

@@ -1,6 +1,7 @@
import { MdSubMenu } from "@material/web/menu/sub-menu";
import { css } from "lit";
import { customElement } from "lit/decorators";
import "element-internals-polyfill";
import { css } from "lit";
import { MdSubMenu } from "@material/web/menu/sub-menu";
@customElement("ha-sub-menu")
export class HaSubMenu extends MdSubMenu {

View File

@@ -206,7 +206,6 @@ export class HaTextField extends TextFieldBase {
.mdc-floating-label,
.mdc-text-field__input[type="number"] {
direction: rtl;
--direction: rtl;
}
`
: css``,

View File

@@ -48,7 +48,7 @@ class HaEntityMarker extends LitElement {
width: 48px;
height: 48px;
font-size: var(--ha-marker-font-size, 1.5em);
border-radius: var(--ha-marker-border-radius, 50%);
border-radius: 50%;
border: 1px solid var(--ha-marker-color, var(--primary-color));
color: var(--primary-text-color);
background-color: var(--card-background-color);

View File

@@ -22,6 +22,7 @@ import {
} from "../../common/dom/setup-leaflet-map";
import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { computeStateName } from "../../common/entity/compute_state_name";
import { loadPolyfillIfNeeded } from "../../resources/resize-observer.polyfill";
import { HomeAssistant, ThemeMode } from "../../types";
import { isTouch } from "../../util/is_touch";
import "../ha-icon-button";
@@ -177,24 +178,16 @@ export class HaMap extends ReactiveElement {
map!.classList.toggle("forced-light", this.themeMode === "light");
}
private _loading = false;
private async _loadMap(): Promise<void> {
if (this._loading) return;
let map = this.shadowRoot!.getElementById("map");
if (!map) {
map = document.createElement("div");
map.id = "map";
this.shadowRoot!.append(map);
}
this._loading = true;
try {
[this.leafletMap, this.Leaflet] = await setupLeafletMap(map);
this._updateMapStyle();
this._loaded = true;
} finally {
this._loading = false;
}
[this.leafletMap, this.Leaflet] = await setupLeafletMap(map);
this._updateMapStyle();
this._loaded = true;
}
public fitMap(options?: { zoom?: number; pad?: number }): void {
@@ -535,6 +528,7 @@ export class HaMap extends ReactiveElement {
private async _attachObserver(): Promise<void> {
if (!this._resizeObserver) {
await loadPolyfillIfNeeded();
this._resizeObserver = new ResizeObserver(() => {
this.leafletMap?.invalidateSize({ debounceMoveend: true });
});

View File

@@ -39,6 +39,7 @@ import {
import { browseLocalMediaPlayer } from "../../data/media_source";
import { isTTSMediaSource } from "../../data/tts";
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
import { loadPolyfillIfNeeded } from "../../resources/resize-observer.polyfill";
import { haStyle } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
import {
@@ -769,6 +770,7 @@ export class HaMediaPlayerBrowse extends LitElement {
private async _attachResizeObserver(): Promise<void> {
if (!this._resizeObserver) {
await loadPolyfillIfNeeded();
this._resizeObserver = new ResizeObserver(
debounce(() => this._measureCard(), 250, false)
);

View File

@@ -79,7 +79,7 @@ class SearchInputOutlined extends LitElement {
}
private async _filterInputChanged(e) {
this._filterChanged(e.target.value?.trim());
this._filterChanged(e.target.value);
}
private async _clearSearch() {

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