Compare commits

..

1 Commits

Author SHA1 Message Date
Paul Bottein
c9152bd077 Don't allow dragging parent into child element 2024-01-30 15:07:58 +01:00
337 changed files with 6597 additions and 13040 deletions

View File

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

View File

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

View File

@@ -26,7 +26,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v4.0.2
uses: actions/setup-node@v4.0.1
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -60,7 +60,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v4.0.2
uses: actions/setup-node@v4.0.1
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -78,7 +78,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v4.0.2
uses: actions/setup-node@v4.0.1
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -89,7 +89,7 @@ jobs:
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@v4.3.1
uses: actions/upload-artifact@v4.3.0
with:
name: frontend-bundle-stats
path: build/stats/*.json
@@ -102,7 +102,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v4.0.2
uses: actions/setup-node@v4.0.1
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -113,7 +113,7 @@ jobs:
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@v4.3.1
uses: actions/upload-artifact@v4.3.0
with:
name: supervisor-bundle-stats
path: build/stats/*.json

View File

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

View File

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

View File

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

View File

@@ -28,7 +28,7 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node
uses: actions/setup-node@v4.0.2
uses: actions/setup-node@v4.0.1
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -57,14 +57,14 @@ jobs:
run: tar -czvf translations.tar.gz translations
- name: Upload build artifacts
uses: actions/upload-artifact@v4.3.1
uses: actions/upload-artifact@v4.3.0
with:
name: wheels
path: dist/home_assistant_frontend*.whl
if-no-files-found: error
- name: Upload translations
uses: actions/upload-artifact@v4.3.1
uses: actions/upload-artifact@v4.3.0
with:
name: translations
path: translations.tar.gz

View File

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

View File

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

View File

@@ -1,13 +0,0 @@
diff --git a/simple-tooltip.js b/simple-tooltip.js
index 78a87f6a223925f0e29fbedb268c85a142ec6985..3d686dd6a3d5a93342b4b01408089fc316b408ca 100644
--- a/simple-tooltip.js
+++ b/simple-tooltip.js
@@ -195,6 +195,8 @@ class SimpleTooltip extends LitElement {
.hidden {
position: absolute;
left: -10000px;
+ inset-inline-start: -10000px;
+ inset-inline-end: initial;
top: auto;
width: 1px;
height: 1px;

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -115,9 +115,7 @@ gulp.task("webpack-prod-app", () =>
gulp.task("webpack-dev-server-demo", () =>
runDevServer({
compiler: webpack(
createDemoConfig({ isProdBuild: false, latestBuild: true })
),
compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })),
contentBase: paths.demo_output_root,
port: 8090,
})
@@ -133,9 +131,7 @@ gulp.task("webpack-prod-demo", () =>
gulp.task("webpack-dev-server-cast", () =>
runDevServer({
compiler: webpack(
createCastConfig({ isProdBuild: false, latestBuild: true })
),
compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })),
contentBase: paths.cast_output_root,
port: 8080,
// Accessible from the network, because that's how Cast hits it.
@@ -178,9 +174,8 @@ gulp.task("webpack-prod-hassio", () =>
gulp.task("webpack-dev-server-gallery", () =>
runDevServer({
compiler: webpack(
createGalleryConfig({ isProdBuild: false, latestBuild: true })
),
// We don't use the es5 build, but the dev server will fuck up the publicPath if we don't
compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })),
contentBase: paths.gallery_output_root,
port: 8100,
listenHost: "0.0.0.0",

View File

@@ -28,6 +28,7 @@ class HcLaunchScreen extends LitElement {
:host {
display: block;
height: 100vh;
padding-top: 64px;
background-color: white;
font-size: 24px;
}
@@ -35,13 +36,17 @@ class HcLaunchScreen extends LitElement {
display: flex;
flex-direction: column;
text-align: center;
align-items: center;
height: 100%;
justify-content: space-evenly;
}
img {
max-width: 80%;
object-fit: cover;
width: 717px;
height: 376px;
display: block;
margin: 0 auto;
}
.status {
padding-right: 54px;
padding-inline-end: 54px;
padding-inline-start: initial;
}
`;
}

View File

@@ -17,7 +17,7 @@ class HcLovelace extends LitElement {
@property({ attribute: false })
public lovelaceConfig!: LovelaceConfig;
@property() public viewPath?: string | number | null;
@property() public viewPath?: string | number;
@property() public urlPath: string | null = null;
@@ -93,9 +93,6 @@ class HcLovelace extends LitElement {
}
private get _viewIndex() {
if (this.viewPath === null) {
return 0;
}
const selectedView = this.viewPath;
const selectedViewInt = parseInt(selectedView as string, 10);
for (let i = 0; i < this.lovelaceConfig.views.length; i++) {

View File

@@ -51,10 +51,10 @@ export class HcMain extends HassElement {
@state() private _lovelacePath: string | number | null = null;
@state() private _urlPath?: string | null;
@state() private _error?: string;
@state() private _urlPath?: string | null;
private _hassUUID?: string;
private _unsubLovelace?: UnsubscribeFunc;
@@ -81,7 +81,7 @@ export class HcMain extends HassElement {
if (
!this._lovelaceConfig ||
this._urlPath === undefined ||
this._lovelacePath === null ||
// Guard against part of HA not being loaded yet.
!this.hass ||
!this.hass.states ||
@@ -99,8 +99,8 @@ export class HcMain extends HassElement {
<hc-lovelace
.hass=${this.hass}
.lovelaceConfig=${this._lovelaceConfig}
.urlPath=${this._urlPath}
.viewPath=${this._lovelacePath}
.urlPath=${this._urlPath}
@config-refresh=${this._generateDefaultLovelaceConfig}
></hc-lovelace>
`;
@@ -226,9 +226,9 @@ export class HcMain extends HassElement {
this.initializeHass(auth, connection);
if (this._hassUUID !== msg.hassUUID) {
this._hassUUID = msg.hassUUID;
this._lovelaceConfig = undefined;
this._urlPath = undefined;
this._lovelacePath = null;
this._urlPath = undefined;
this._lovelaceConfig = undefined;
if (this._unsubLovelace) {
this._unsubLovelace();
this._unsubLovelace = undefined;
@@ -270,7 +270,7 @@ export class HcMain extends HassElement {
}
this._error = undefined;
if (msg.urlPath === "lovelace" || msg.urlPath === undefined) {
if (msg.urlPath === "lovelace") {
msg.urlPath = null;
}
this._lovelacePath = msg.viewPath;
@@ -285,7 +285,7 @@ export class HcMain extends HassElement {
],
};
this._urlPath = "energy";
this._lovelacePath = null;
this._lovelacePath = 0;
this._sendStatus();
return;
}

View File

@@ -17,14 +17,12 @@ import { energyEntities } from "./stubs/entities";
import { mockEntityRegistry } from "./stubs/entity_registry";
import { mockEvents } from "./stubs/events";
import { mockFrontend } from "./stubs/frontend";
import { mockIcons } from "./stubs/icons";
import { mockHistory } from "./stubs/history";
import { mockLovelace } from "./stubs/lovelace";
import { mockMediaPlayer } from "./stubs/media_player";
import { mockPersistentNotification } from "./stubs/persistent_notification";
import { mockRecorder } from "./stubs/recorder";
import { mockTodo } from "./stubs/todo";
import { mockSensor } from "./stubs/sensor";
import { mockSystemLog } from "./stubs/system_log";
import { mockTemplate } from "./stubs/template";
import { mockTranslations } from "./stubs/translations";
@@ -52,13 +50,11 @@ export class HaDemo extends HomeAssistantAppEl {
mockHistory(hass);
mockRecorder(hass);
mockTodo(hass);
mockSensor(hass);
mockSystemLog(hass);
mockTemplate(hass);
mockEvents(hass);
mockMediaPlayer(hass);
mockFrontend(hass);
mockIcons(hass);
mockEnergy(hass);
mockPersistentNotification(hass);
mockConfigEntries(hass);

View File

@@ -1,33 +0,0 @@
import { IconCategory } from "../../../src/data/icons";
import { ENTITY_COMPONENT_ICONS } from "../../../src/fake_data/entity_component_icons";
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockIcons = (hass: MockHomeAssistant) => {
hass.mockWS(
"frontend/get_icons",
async ({
category,
integration,
}: {
category: IconCategory;
integration?: string;
}) => {
if (integration) {
try {
const response = await fetch(
`https://raw.githubusercontent.com/home-assistant/core/dev/homeassistant/components/${integration}/icons.json`
).then((resp) => resp.json());
return { resources: { [integration]: response[category] || {} } };
} catch {
return { resources: {} };
}
}
if (category === "entity_component") {
return {
resources: ENTITY_COMPONENT_ICONS,
};
}
return { resources: {} };
}
);
};

View File

@@ -1,58 +0,0 @@
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockSensor = (hass: MockHomeAssistant) => {
hass.mockWS("sensor/numeric_device_classes", () => [
{
numeric_device_classes: [
"volume_storage",
"gas",
"data_size",
"irradiance",
"wind_speed",
"volatile_organic_compounds",
"volatile_organic_compounds_parts",
"voltage",
"frequency",
"precipitation_intensity",
"volume",
"precipitation",
"battery",
"nitrogen_dioxide",
"speed",
"signal_strength",
"pm1",
"nitrous_oxide",
"atmospheric_pressure",
"data_rate",
"temperature",
"power_factor",
"aqi",
"current",
"volume_flow_rate",
"humidity",
"duration",
"ozone",
"distance",
"pressure",
"pm25",
"weight",
"energy",
"carbon_monoxide",
"apparent_power",
"illuminance",
"energy_storage",
"moisture",
"power",
"water",
"carbon_dioxide",
"ph",
"reactive_power",
"monetary",
"nitrogen_monoxide",
"pm10",
"sound_pressure",
"sulphur_dioxide",
],
},
]);
};

View File

@@ -21,5 +21,4 @@ export const mockTodo = (hass: MockHomeAssistant) => {
},
] as TodoItem[],
}));
hass.mockWS("todo/item/subscribe", (_msg, _hass) => () => {});
};

View File

@@ -1,5 +1,5 @@
import { Button } from "@material/mwc-button";
import { html, LitElement, css, TemplateResult, nothing } from "lit";
import { html, LitElement, css, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
import { fireEvent } from "../../../src/common/dom/fire_event";
@@ -9,7 +9,7 @@ import "../../../src/components/ha-card";
class DemoBlackWhiteRow extends LitElement {
@property() title!: string;
@property() value?: any;
@property() value!: any;
@property({ type: Boolean }) public disabled = false;
@@ -45,9 +45,7 @@ class DemoBlackWhiteRow extends LitElement {
</mwc-button>
</div>
</ha-card>
${this.value
? html`<pre>${JSON.stringify(this.value, undefined, 2)}</pre>`
: nothing}
<pre>${JSON.stringify(this.value, undefined, 2)}</pre>
</div>
</div>
`;

View File

@@ -17,7 +17,6 @@ export const basicTrace: DemoTrace = {
{
path: "trigger/0",
timestamp: "2021-03-25T04:36:51.223693+00:00",
changed_variables: {},
},
],
"condition/0": [

View File

@@ -17,7 +17,6 @@ export const motionLightTrace: DemoTrace = {
{
path: "trigger/0",
timestamp: "2021-03-25T04:36:51.223693+00:00",
changed_variables: {},
},
],
"action/0": [

View File

@@ -3,6 +3,7 @@
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/components/trace/hat-script-graph";
import "../../../../src/components/trace/hat-trace-timeline";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../../src/types";
@@ -55,7 +56,6 @@ export class DemoAutomationTraceTimeline extends LitElement {
super.firstUpdated(changedProps);
const hass = provideHass(this);
hass.updateTranslations(null, "en");
hass.updateTranslations("config", "en");
}
static get styles() {

View File

@@ -60,7 +60,6 @@ export class DemoAutomationTrace extends LitElement {
super.firstUpdated(changedProps);
const hass = provideHass(this);
hass.updateTranslations(null, "en");
hass.updateTranslations("config", "en");
}
static get styles() {

View File

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

View File

@@ -9,7 +9,6 @@ import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
import "../../../../src/components/ha-selector/ha-selector";
import "../../../../src/components/ha-settings-row";
import type { AreaRegistryEntry } from "../../../../src/data/area_registry";
import { BlueprintInput } from "../../../../src/data/blueprint";
import { showDialog } from "../../../../src/dialogs/make-dialog-manager";
import { getEntity } from "../../../../src/fake_data/entity";
@@ -94,25 +93,22 @@ const DEVICES = [
},
];
const AREAS: AreaRegistryEntry[] = [
const AREAS = [
{
area_id: "backyard",
name: "Backyard",
icon: null,
picture: null,
aliases: [],
},
{
area_id: "bedroom",
name: "Bedroom",
icon: "mdi:bed",
picture: null,
aliases: [],
},
{
area_id: "livingroom",
name: "Livingroom",
icon: "mdi:sofa",
picture: null,
aliases: [],
},
@@ -275,14 +271,6 @@ const SCHEMAS: {
selector: { color_temp: {} },
},
color_rgb: { name: "Color", selector: { color_rgb: {} } },
qr_code: {
name: "QR Code",
selector: { qr_code: { data: "https://home-assistant.io" } },
},
constant: {
name: "Constant",
selector: { constant: { value: true, label: "Yes!" } },
},
},
},
{
@@ -509,7 +497,7 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
this.requestUpdate();
};
return html`
<demo-black-white-row .title=${info.name}>
<demo-black-white-row .title=${info.name} .value=${this.data[idx]}>
${["light", "dark"].map((slot) =>
Object.entries(info.input).map(
([key, value]) => html`
@@ -542,8 +530,8 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
}
static styles = css`
ha-settings-row {
--paper-item-body-two-line-min-height: 0;
ha-selector {
width: 60;
}
.options {
max-width: 800px;

View File

@@ -3,7 +3,6 @@ import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("alarm_control_panel", "alarm", "disarmed", {
@@ -85,7 +84,6 @@ class DemoAlarmPanelEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,7 +3,6 @@ import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("light", "bed_light", "on", {
@@ -147,7 +146,6 @@ class DemoArea extends LitElement {
entity_id: "binary_sensor.kitchen_door",
},
]);
mockIcons(hass);
}
}

View File

@@ -3,7 +3,6 @@ import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("light", "controller_1", "on", {
@@ -67,7 +66,6 @@ class DemoConditional extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,7 +3,6 @@ import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("light", "bed_light", "on", {
@@ -324,7 +323,6 @@ class DemoEntities extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,7 +3,6 @@ import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("light", "bed_light", "on", {
@@ -83,7 +82,6 @@ class DemoButtonEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,7 +3,6 @@ import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("device_tracker", "demo_paulus", "work", {
@@ -124,7 +123,6 @@ class DemoEntityFilter extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,7 +3,6 @@ import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("sensor", "brightness", "12", {}),
@@ -129,7 +128,6 @@ class DemoGaugeEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,7 +3,6 @@ import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("device_tracker", "demo_paulus", "home", {
@@ -239,7 +238,6 @@ class DemoGlanceEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -4,7 +4,6 @@ import { mockHistory } from "../../../../demo/src/stubs/history";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("light", "kitchen_lights", "on", {
@@ -215,7 +214,6 @@ class DemoStack extends LitElement {
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockHistory(hass);
mockIcons(hass);
}
}

View File

@@ -3,7 +3,6 @@ import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("light", "bed_light", "on", {
@@ -77,7 +76,6 @@ class DemoLightEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,7 +3,6 @@ import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("light", "bed_light", "on", {
@@ -139,7 +138,6 @@ class DemoPictureElements extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,7 +3,6 @@ import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("light", "kitchen_lights", "on", {
@@ -94,7 +93,6 @@ class DemoPictureEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,7 +3,6 @@ import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("switch", "decorative_lights", "on", {
@@ -135,7 +134,6 @@ class DemoPictureGlance extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,7 +3,6 @@ import { customElement, query } from "lit/decorators";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { createPlantEntities } from "../../data/plants";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const CONFIGS = [
{
@@ -44,7 +43,6 @@ export class DemoPlantEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createPlantEntities());
mockIcons(hass);
}
}

View File

@@ -3,7 +3,6 @@ import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("climate", "ecobee", "auto", {
@@ -117,7 +116,6 @@ class DemoThermostatEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -6,7 +6,6 @@ import { VacuumEntityFeature } from "../../../../src/data/vacuum";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("switch", "tv_outlet", "on", {
@@ -80,18 +79,6 @@ const CONFIGS = [
color: pink
`,
},
{
heading: "Whole tile tap action",
config: `
- type: tile
entity: switch.tv_outlet
color: pink
tap_action:
action: toggle
icon_tap_action:
action: none
`,
},
{
heading: "Unknown entity",
config: `
@@ -185,7 +172,6 @@ class DemoTile extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -4,7 +4,6 @@ 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", {
@@ -48,7 +47,6 @@ class DemoTodoListEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
mockTodo(hass);
}

View File

@@ -11,7 +11,6 @@ import "../../../../src/components/data-table/ha-data-table";
import type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table";
import "../../../../src/components/entity/state-badge";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import { mockIcons } from "../../../../demo/src/stubs/icons";
import { HomeAssistant } from "../../../../src/types";
const SENSOR_DEVICE_CLASSES = [
@@ -292,7 +291,6 @@ const ENTITIES: HassEntity[] = [
createEntity("water_heater.high_demand", "high_demand"),
createEntity("water_heater.heat_pump", "heat_pump"),
createEntity("water_heater.gas", "gas"),
createEntity("select.speed", "ridiculous_speed"),
];
function createEntity(
@@ -399,16 +397,6 @@ export class DemoEntityState extends LitElement {
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
const hass = provideHass(this);
mockIcons(hass);
hass.updateHass({
entities: {
"select.speed": {
entity_id: "select.speed",
translation_key: "speed",
platform: "demo",
},
},
});
hass.updateTranslations(null, "en");
hass.updateTranslations("config", "en");
}

View File

@@ -1263,7 +1263,6 @@ class HassioAddonInfo extends LitElement {
.card-actions {
justify-content: space-between;
display: flex;
direction: var(--direction);
}
.changelog {
display: contents;

View File

@@ -154,16 +154,12 @@ class HassioHardwareDialog extends LitElement {
ha-icon-button {
position: absolute;
right: 16px;
inset-inline-end: 16px;
inset-inline-start: initial;
top: 10px;
text-decoration: none;
color: var(--primary-text-color);
}
h2 {
margin: 18px 42px 0 18px;
margin-inline-start: 18px;
margin-inline-end: 42px;
color: var(--primary-text-color);
}

View File

@@ -1,5 +1,7 @@
import "@material/mwc-button/mwc-button";
import { mdiDelete, mdiDeleteOff } from "@mdi/js";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
@@ -25,8 +27,6 @@ import type { HomeAssistant } from "../../../../src/types";
import { HassioRepositoryDialogParams } from "./show-dialog-repositories";
import type { HaTextField } from "../../../../src/components/ha-textfield";
import "../../../../src/components/ha-textfield";
import "../../../../src/components/ha-list-new";
import "../../../../src/components/ha-list-item-new";
@customElement("dialog-hassio-repositories")
class HassioRepositoriesDialog extends LitElement {
@@ -106,46 +106,44 @@ class HassioRepositoriesDialog extends LitElement {
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: ""}
<div class="form">
<ha-list-new>
${repositories.length
? repositories.map(
(repo) => html`
<ha-list-item-new class="option">
${repo.name}
<div slot="supporting-text">
<div>${repo.maintainer}</div>
<div>${repo.url}</div>
</div>
<div class="delete" slot="end">
<ha-icon-button
.label=${this._dialogParams!.supervisor.localize(
"dialog.repositories.remove"
)}
.disabled=${usedRepositories.includes(repo.slug)}
.slug=${repo.slug}
.path=${usedRepositories.includes(repo.slug)
? mdiDeleteOff
: mdiDelete}
@click=${this._removeRepository}
>
</ha-icon-button>
<simple-tooltip
animation-delay="0"
position="bottom"
offset="1"
>
${this._dialogParams!.supervisor.localize(
usedRepositories.includes(repo.slug)
? "dialog.repositories.used"
: "dialog.repositories.remove"
)}
</simple-tooltip>
</div>
</ha-list-item-new>
`
)
: html`<ha-list-item-new> No repositories </ha-list-item-new>`}
</ha-list-new>
${repositories.length
? repositories.map(
(repo) => html`
<paper-item class="option">
<paper-item-body three-line>
<div>${repo.name}</div>
<div secondary>${repo.maintainer}</div>
<div secondary>${repo.url}</div>
</paper-item-body>
<div class="delete">
<ha-icon-button
.label=${this._dialogParams!.supervisor.localize(
"dialog.repositories.remove"
)}
.disabled=${usedRepositories.includes(repo.slug)}
.slug=${repo.slug}
.path=${usedRepositories.includes(repo.slug)
? mdiDeleteOff
: mdiDelete}
@click=${this._removeRepository}
>
</ha-icon-button>
<simple-tooltip
animation-delay="0"
position="bottom"
offset="1"
>
${this._dialogParams!.supervisor.localize(
usedRepositories.includes(repo.slug)
? "dialog.repositories.used"
: "dialog.repositories.remove"
)}
</simple-tooltip>
</div>
</paper-item>
`
)
: html`<paper-item> No repositories </paper-item>`}
<div class="layout horizontal bottom">
<ha-textfield
class="flex-auto"
@@ -208,9 +206,6 @@ class HassioRepositoriesDialog extends LitElement {
div.delete ha-icon-button {
color: var(--error-color);
}
ha-list-item-new {
position: relative;
}
`,
];
}

View File

@@ -29,11 +29,11 @@
"@braintree/sanitize-url": "7.0.0",
"@codemirror/autocomplete": "6.12.0",
"@codemirror/commands": "6.3.3",
"@codemirror/language": "6.10.1",
"@codemirror/language": "6.10.0",
"@codemirror/legacy-modes": "6.3.3",
"@codemirror/search": "6.5.6",
"@codemirror/state": "6.4.1",
"@codemirror/view": "6.24.1",
"@codemirror/search": "6.5.5",
"@codemirror/state": "6.4.0",
"@codemirror/view": "6.23.1",
"@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "6.12.2",
"@formatjs/intl-displaynames": "6.6.6",
@@ -43,18 +43,18 @@
"@formatjs/intl-numberformat": "8.10.0",
"@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",
"@fullcalendar/list": "6.1.11",
"@fullcalendar/luxon3": "6.1.11",
"@fullcalendar/timegrid": "6.1.11",
"@fullcalendar/core": "6.1.10",
"@fullcalendar/daygrid": "6.1.10",
"@fullcalendar/interaction": "6.1.10",
"@fullcalendar/list": "6.1.10",
"@fullcalendar/luxon3": "6.1.10",
"@fullcalendar/timegrid": "6.1.10",
"@lezer/highlight": "1.2.0",
"@lit-labs/context": "0.4.1",
"@lit-labs/motion": "1.0.7",
"@lit-labs/motion": "1.0.6",
"@lit-labs/observers": "2.0.2",
"@lit-labs/virtualizer": "2.0.12",
"@lrnwebcomponents/simple-tooltip": "patch:@lrnwebcomponents/simple-tooltip@npm%3A8.0.0#~/.yarn/patches/@lrnwebcomponents-simple-tooltip-npm-8.0.0-77591f2e0c.patch",
"@lrnwebcomponents/simple-tooltip": "8.0.0",
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
"@material/mwc-base": "0.27.0",
@@ -72,7 +72,6 @@
"@material/mwc-radio": "0.27.0",
"@material/mwc-ripple": "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",
@@ -81,16 +80,17 @@
"@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.3.0",
"@material/web": "=1.2.0",
"@mdi/js": "7.4.47",
"@mdi/svg": "7.4.47",
"@polymer/paper-item": "3.0.1",
"@polymer/paper-listbox": "3.0.1",
"@polymer/paper-tabs": "3.1.0",
"@polymer/paper-toast": "3.0.1",
"@polymer/polymer": "3.5.1",
"@thomasloven/round-slider": "0.6.0",
"@vaadin/combo-box": "24.3.7",
"@vaadin/vaadin-themable-mixin": "24.3.7",
"@vaadin/combo-box": "24.3.4",
"@vaadin/vaadin-themable-mixin": "24.3.4",
"@vibrant/color": "3.2.1-alpha.1",
"@vibrant/core": "3.2.1-alpha.1",
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
@@ -99,9 +99,8 @@
"@webcomponents/webcomponentsjs": "2.8.0",
"app-datepicker": "5.1.1",
"chart.js": "4.4.1",
"color-name": "2.0.0",
"comlink": "4.4.1",
"core-js": "3.36.0",
"core-js": "3.35.1",
"cropperjs": "1.6.1",
"date-fns": "2.30.0",
"date-fns-tz": "2.0.0",
@@ -110,7 +109,7 @@
"element-internals-polyfill": "1.3.10",
"fuse.js": "7.0.0",
"google-timezones-json": "1.2.0",
"hls.js": "1.5.7",
"hls.js": "1.5.2",
"home-assistant-js-websocket": "9.1.0",
"idb-keyval": "6.2.1",
"intl-messageformat": "10.5.11",
@@ -119,7 +118,7 @@
"leaflet-draw": "1.0.4",
"lit": "2.8.0",
"luxon": "3.4.4",
"marked": "12.0.0",
"marked": "11.2.0",
"memoize-one": "6.0.0",
"node-vibrant": "3.2.1-alpha.1",
"proxy-polyfill": "0.3.2",
@@ -156,11 +155,11 @@
"@babel/plugin-transform-runtime": "7.23.9",
"@babel/preset-env": "7.23.9",
"@babel/preset-typescript": "7.23.3",
"@bundle-stats/plugin-webpack-filter": "4.10.1",
"@bundle-stats/plugin-webpack-filter": "4.9.2",
"@koa/cors": "5.0.0",
"@lokalise/node-api": "12.1.0",
"@octokit/auth-oauth-device": "7.0.0",
"@octokit/plugin-retry": "7.0.1",
"@octokit/auth-oauth-device": "6.0.1",
"@octokit/plugin-retry": "6.0.1",
"@octokit/rest": "20.0.2",
"@open-wc/dev-server-hmr": "0.1.4",
"@rollup/plugin-babel": "6.0.4",
@@ -171,7 +170,6 @@
"@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.13",
"@types/chromecast-caf-sender": "1.0.8",
"@types/color-name": "1.1.3",
"@types/glob": "8.1.0",
"@types/html-minifier-terser": "7.0.2",
"@types/js-yaml": "4.0.9",
@@ -181,19 +179,19 @@
"@types/mocha": "10.0.6",
"@types/qrcode": "1.5.5",
"@types/serve-handler": "6.1.4",
"@types/sortablejs": "1.15.8",
"@types/sortablejs": "1.15.7",
"@types/tar": "6.1.11",
"@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29",
"@typescript-eslint/eslint-plugin": "7.1.0",
"@typescript-eslint/parser": "7.1.0",
"@typescript-eslint/eslint-plugin": "6.19.1",
"@typescript-eslint/parser": "6.19.1",
"@web/dev-server": "0.1.38",
"@web/dev-server-rollup": "0.4.1",
"babel-loader": "9.1.3",
"babel-plugin-template-html-minifier": "4.1.0",
"chai": "5.1.0",
"chai": "5.0.3",
"del": "7.1.0",
"eslint": "8.57.0",
"eslint": "8.56.0",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-airbnb-typescript": "17.1.0",
"eslint-config-prettier": "9.1.0",
@@ -201,32 +199,32 @@
"eslint-plugin-disable": "2.0.3",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-lit": "1.11.0",
"eslint-plugin-lit-a11y": "4.1.2",
"eslint-plugin-unused-imports": "3.1.0",
"eslint-plugin-lit-a11y": "4.1.1",
"eslint-plugin-unused-imports": "3.0.0",
"eslint-plugin-wc": "2.0.4",
"fancy-log": "2.0.0",
"fs-extra": "11.2.0",
"glob": "10.3.10",
"gulp": "4.0.2",
"gulp-flatmap": "1.0.2",
"gulp-json-transform": "0.5.0",
"gulp-json-transform": "0.4.8",
"gulp-merge-json": "2.1.2",
"gulp-rename": "2.0.0",
"gulp-zopfli-green": "6.0.1",
"html-minifier-terser": "7.2.0",
"husky": "9.0.11",
"husky": "9.0.6",
"instant-mocha": "1.5.2",
"jszip": "3.10.1",
"lint-staged": "15.2.2",
"lint-staged": "15.2.0",
"lit-analyzer": "2.0.3",
"lodash.template": "4.5.0",
"magic-string": "0.30.7",
"magic-string": "0.30.5",
"map-stream": "0.0.7",
"mocha": "10.3.0",
"mocha": "10.2.0",
"object-hash": "3.0.0",
"open": "10.0.4",
"open": "10.0.3",
"pinst": "3.0.0",
"prettier": "3.2.5",
"prettier": "3.2.4",
"rollup": "2.79.1",
"rollup-plugin-string": "3.0.0",
"rollup-plugin-terser": "7.0.2",
@@ -242,12 +240,12 @@
"typescript": "5.3.3",
"vinyl-buffer": "1.0.1",
"vinyl-source-stream": "2.0.0",
"webpack": "5.90.3",
"webpack": "5.90.0",
"webpack-cli": "5.1.4",
"webpack-dev-server": "5.0.2",
"webpack-dev-server": "4.15.1",
"webpack-manifest-plugin": "5.0.0",
"webpack-stats-plugin": "1.1.3",
"webpackbar": "6.0.1",
"webpackbar": "6.0.0",
"workbox-build": "7.0.0"
},
"_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch",
@@ -257,8 +255,8 @@
"lit": "2.8.0",
"clean-css": "5.3.3",
"@lit/reactive-element": "1.6.3",
"sortablejs@1.15.2": "patch:sortablejs@npm%3A1.15.2#~/.yarn/patches/sortablejs-npm-1.15.2-73347ae85a.patch",
"sortablejs@1.15.0": "patch:sortablejs@npm%3A1.15.0#./.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.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.1.0"
"packageManager": "yarn@4.0.2"
}

View File

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

View File

@@ -40,7 +40,6 @@ if [ -n "$ref" ]; then
echo "Installing Home Assistant core at ${ref}..."
python3 -m pip install --user --upgrade --src "$HOME/src" \
--editable "git+${coreURL}@${ref}#egg=homeassistant"
(cd ~/src/homeassistant && exec python3 -m script.translations develop --all)
fi
if [ ! -d "${WD}/config" ]; then

View File

@@ -1,25 +1,19 @@
import { PageNavigation } from "../../layouts/hass-tabs-subpage";
import { HomeAssistant } from "../../types";
import { ensureArray } from "../array/ensure-array";
import { isComponentLoaded } from "./is_component_loaded";
export const canShowPage = (hass: HomeAssistant, page: PageNavigation) =>
(isCore(page) || isLoadedIntegration(hass, page)) &&
!hideAdvancedPage(hass, page) &&
isNotLoadedIntegration(hass, page);
!hideAdvancedPage(hass, page);
const isLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) =>
!page.component ||
ensureArray(page.component).some((integration) =>
isComponentLoaded(hass, integration)
);
const isNotLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) =>
!page.not_component ||
!ensureArray(page.not_component).some((integration) =>
isComponentLoaded(hass, integration)
);
page.component
? isComponentLoaded(hass, page.component)
: page.components
? page.components.some((integration) =>
isComponentLoaded(hass, integration)
)
: true;
const isCore = (page: PageNavigation) => page.core;
const isAdvancedPage = (page: PageNavigation) => page.advancedOnly;
const userWantsAdvanced = (hass: HomeAssistant) => hass.userData?.showAdvanced;

View File

@@ -1,13 +1,8 @@
import { MAIN_WINDOW_NAME } from "../../data/main_window";
export const mainWindow = (() => {
try {
return window.name === MAIN_WINDOW_NAME
? window
: parent.name === MAIN_WINDOW_NAME
? parent
: top!;
} catch {
return window;
}
})();
export const mainWindow =
window.name === MAIN_WINDOW_NAME
? window
: parent.name === MAIN_WINDOW_NAME
? parent
: top!;

View File

@@ -53,7 +53,9 @@ export const computeAttributeValueDisplay = (
if (domain === "weather") {
unit = getWeatherUnit(config, stateObj as WeatherEntity, attribute);
} else if (TEMPERATURE_ATTRIBUTES.has(attribute)) {
}
if (TEMPERATURE_ATTRIBUTES.has(attribute)) {
unit = config.unit_system.temperature;
}

View File

@@ -20,14 +20,14 @@ function findNestedItem(
}, obj);
}
export function nestedArrayMove<A>(
obj: A,
export function nestedArrayMove<T>(
obj: T | T[],
oldIndex: number,
newIndex: number,
oldPath?: ItemPath,
newPath?: ItemPath
): A {
const newObj = (Array.isArray(obj) ? [...obj] : { ...obj }) as A;
): T | T[] {
const newObj = Array.isArray(obj) ? [...obj] : { ...obj };
const from = oldPath ? findNestedItem(newObj, oldPath) : newObj;
const to = newPath ? findNestedItem(newObj, newPath, true) : newObj;

View File

@@ -38,8 +38,4 @@ export function setDirectionStyles(direction: string, element: LitElement) {
"--margin-title",
direction === "ltr" ? "var(--margin-title-ltr)" : "var(--margin-title-rtl)"
);
element.style.setProperty(
"--scale-direction",
direction === "ltr" ? "1" : "-1"
);
}

View File

@@ -5,19 +5,12 @@ import type {
ChartOptions,
TooltipModel,
} from "chart.js";
import {
css,
CSSResultGroup,
html,
nothing,
LitElement,
PropertyValues,
} from "lit";
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import { fireEvent } from "../../common/dom/fire_event";
import { clamp } from "../../common/number/clamp";
import { computeRTL } from "../../common/util/compute_rtl";
import { HomeAssistant } from "../../types";
import { debounce } from "../../common/util/debounce";
@@ -35,11 +28,6 @@ interface Tooltip
left: string;
}
export interface ChartDatasetExtra {
show_legend?: boolean;
legend_label?: string;
}
@customElement("ha-chart-base")
export class HaChartBase extends LitElement {
public chart?: Chart;
@@ -51,8 +39,6 @@ export class HaChartBase extends LitElement {
@property({ attribute: false }) public data: ChartData = { datasets: [] };
@property({ attribute: false }) public extraData?: ChartDatasetExtra[];
@property({ attribute: false }) public options?: ChartOptions;
@property({ attribute: false }) public plugins?: any[];
@@ -61,8 +47,6 @@ export class HaChartBase extends LitElement {
@property({ type: Number }) public paddingYAxis = 0;
@property({ type: Boolean }) public externalHidden = false;
@state() private _chartHeight?: number;
@state() private _tooltip?: Tooltip;
@@ -75,8 +59,6 @@ export class HaChartBase extends LitElement {
private _paddingYAxisInternal = 0;
private _datasetOrder: number[] = [];
public disconnectedCallback() {
super.disconnectedCallback();
this._releaseCanvas();
@@ -167,29 +149,6 @@ export class HaChartBase extends LitElement {
}
}
// put the legend labels in sorted order if provided
if (changedProps.has("data")) {
this._datasetOrder = this.data.datasets.map((_, index) => index);
if (this.data?.datasets.some((dataset) => dataset.order)) {
this._datasetOrder.sort(
(a, b) =>
(this.data.datasets[a].order || 0) -
(this.data.datasets[b].order || 0)
);
}
if (this.externalHidden) {
this._hiddenDatasets = new Set();
if (this.data?.datasets) {
this.data.datasets.forEach((dataset, index) => {
if (dataset.hidden) {
this._hiddenDatasets.add(index);
}
});
}
}
}
if (!this.hasUpdated || !this.chart) {
return;
}
@@ -199,7 +158,7 @@ export class HaChartBase extends LitElement {
return;
}
if (changedProps.has("data")) {
if (this._hiddenDatasets.size && !this.externalHidden) {
if (this._hiddenDatasets.size) {
this.data.datasets.forEach((dataset, index) => {
dataset.hidden = this._hiddenDatasets.has(index);
});
@@ -217,32 +176,26 @@ export class HaChartBase extends LitElement {
${this.options?.plugins?.legend?.display === true
? html`<div class="chartLegend">
<ul>
${this._datasetOrder.map((index) => {
const dataset = this.data.datasets[index];
return this.extraData?.[index]?.show_legend === false
? nothing
: html`<li
.datasetIndex=${index}
@click=${this._legendClick}
class=${classMap({
hidden: this._hiddenDatasets.has(index),
${this.data.datasets.map(
(dataset, index) =>
html`<li
.datasetIndex=${index}
@click=${this._legendClick}
class=${classMap({
hidden: this._hiddenDatasets.has(index),
})}
.title=${dataset.label}
>
<div
class="bullet"
style=${styleMap({
backgroundColor: dataset.backgroundColor as string,
borderColor: dataset.borderColor as string,
})}
.title=${this.extraData?.[index]?.legend_label ??
dataset.label}
>
<div
class="bullet"
style=${styleMap({
backgroundColor: dataset.backgroundColor as string,
borderColor: dataset.borderColor as string,
})}
></div>
<div class="label">
${this.extraData?.[index]?.legend_label ??
dataset.label}
</div>
</li>`;
})}
></div>
<div class="label">${dataset.label}</div>
</li>`
)}
</ul>
</div>`
: ""}
@@ -259,10 +212,12 @@ export class HaChartBase extends LitElement {
height: `${
this.height ?? this._chartHeight ?? this.clientWidth / 2
}px`,
"padding-left": `${this._paddingYAxisInternal}px`,
"padding-right": 0,
"padding-inline-start": `${this._paddingYAxisInternal}px`,
"padding-inline-end": 0,
"padding-left": `${
computeRTL(this.hass) ? 0 : this._paddingYAxisInternal
}px`,
"padding-right": `${
computeRTL(this.hass) ? this._paddingYAxisInternal : 0
}px`,
})}
>
<canvas></canvas>
@@ -387,19 +342,9 @@ export class HaChartBase extends LitElement {
if (this.chart.isDatasetVisible(index)) {
this.chart.setDatasetVisibility(index, false);
this._hiddenDatasets.add(index);
if (this.externalHidden) {
fireEvent(this, "dataset-hidden", {
index,
});
}
} else {
this.chart.setDatasetVisibility(index, true);
this._hiddenDatasets.delete(index);
if (this.externalHidden) {
fireEvent(this, "dataset-unhidden", {
index,
});
}
}
this.chart.update("none");
this.requestUpdate("_hiddenDatasets");
@@ -488,6 +433,14 @@ export class HaChartBase extends LitElement {
.chartTooltip .bullet {
align-self: baseline;
}
:host([rtl]) .chartLegend .bullet,
:host([rtl]) .chartTooltip .bullet {
margin-right: inherit;
margin-left: 6px;
margin-inline-end: inherit;
margin-inline-start: 6px;
direction: var(--direction);
}
.chartTooltip {
padding: 8px;
font-size: 90%;
@@ -496,13 +449,12 @@ export class HaChartBase extends LitElement {
color: white;
border-radius: 4px;
pointer-events: none;
z-index: 1;
-ms-user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
z-index: 1000;
width: 200px;
box-sizing: border-box;
direction: var(--direction);
}
:host([rtl]) .chartTooltip {
direction: rtl;
}
.chartLegend ul,
.chartTooltip ul {
@@ -544,8 +496,4 @@ declare global {
interface HTMLElementTagNameMap {
"ha-chart-base": HaChartBase;
}
interface HASSDomEvents {
"dataset-hidden": { index: number };
"dataset-unhidden": { index: number };
}
}

View File

@@ -47,12 +47,6 @@ export class StateHistoryChartLine extends LitElement {
@property({ type: Boolean }) public logarithmicScale = false;
@property({ type: Number }) public minYAxis?: number;
@property({ type: Number }) public maxYAxis?: number;
@property({ type: Boolean }) public fitYData = false;
@state() private _chartData?: ChartData<"line">;
@state() private _entityIds: string[] = [];
@@ -90,10 +84,7 @@ export class StateHistoryChartLine extends LitElement {
changedProps.has("startTime") ||
changedProps.has("endTime") ||
changedProps.has("unit") ||
changedProps.has("logarithmicScale") ||
changedProps.has("minYAxis") ||
changedProps.has("maxYAxis") ||
changedProps.has("fitYData")
changedProps.has("logarithmicScale")
) {
this._chartOptions = {
parsing: false,
@@ -111,7 +102,7 @@ export class StateHistoryChartLine extends LitElement {
config: this.hass.config,
},
},
min: this.startTime,
suggestedMin: this.startTime,
suggestedMax: this.endTime,
ticks: {
maxRotation: 0,
@@ -130,10 +121,6 @@ export class StateHistoryChartLine extends LitElement {
},
},
y: {
suggestedMin: this.fitYData ? this.minYAxis : null,
suggestedMax: this.fitYData ? this.maxYAxis : null,
min: this.fitYData ? null : this.minYAxis,
max: this.fitYData ? null : this.maxYAxis,
ticks: {
maxTicksLimit: 7,
},
@@ -220,12 +207,7 @@ export class StateHistoryChartLine extends LitElement {
// @ts-expect-error
locale: numberFormatToLocale(this.hass.locale),
onClick: (e: any) => {
if (
!this.clickForMoreInfo ||
!(e.native instanceof MouseEvent) ||
(e.native instanceof PointerEvent &&
e.native.pointerType !== "mouse")
) {
if (!this.clickForMoreInfo) {
return;
}

View File

@@ -114,7 +114,7 @@ export class StateHistoryChartTimeline extends LitElement {
config: this.hass.config,
},
},
min: this.startTime,
suggestedMin: this.startTime,
suggestedMax: this.endTime,
ticks: {
autoSkip: true,
@@ -224,11 +224,7 @@ export class StateHistoryChartTimeline extends LitElement {
// @ts-expect-error
locale: numberFormatToLocale(this.hass.locale),
onClick: (e: any) => {
if (
!this.clickForMoreInfo ||
!(e.native instanceof MouseEvent) ||
(e.native instanceof PointerEvent && e.native.pointerType !== "mouse")
) {
if (!this.clickForMoreInfo) {
return;
}

View File

@@ -74,12 +74,6 @@ export class StateHistoryCharts extends LitElement {
@property({ type: Boolean }) public logarithmicScale = false;
@property({ type: Number }) public minYAxis?: number;
@property({ type: Number }) public maxYAxis?: number;
@property({ type: Boolean }) public fitYData = false;
private _computedStartTime!: Date;
private _computedEndTime!: Date;
@@ -167,9 +161,6 @@ export class StateHistoryCharts extends LitElement {
.chartIndex=${index}
.clickForMoreInfo=${this.clickForMoreInfo}
.logarithmicScale=${this.logarithmicScale}
.minYAxis=${this.minYAxis}
.maxYAxis=${this.maxYAxis}
.fitYData=${this.fitYData}
@y-width-changed=${this._yWidthChanged}
></state-history-chart-line>
</div> `;
@@ -233,32 +224,16 @@ export class StateHistoryCharts extends LitElement {
new Date().getTime() - 60 * 60 * this.hoursToShow * 1000
);
} else {
let minTimeAll = (this.historyData?.timeline ?? []).reduce(
(minTime, stateInfo) =>
Math.min(
minTime,
new Date(stateInfo.data[0].last_changed).getTime()
),
new Date().getTime()
this._computedStartTime = new Date(
(this.historyData?.timeline ?? []).reduce(
(minTime, stateInfo) =>
Math.min(
minTime,
new Date(stateInfo.data[0].last_changed).getTime()
),
new Date().getTime()
)
);
minTimeAll = (this.historyData?.line ?? []).reduce(
(minTimeLine, line) =>
Math.min(
minTimeLine,
line.data.reduce(
(minTimeData, data) =>
Math.min(
minTimeData,
new Date(data.states[0].last_changed).getTime()
),
minTimeLine
)
),
minTimeAll
);
this._computedStartTime = new Date(minTimeAll);
}
}
}

View File

@@ -32,11 +32,7 @@ import {
} from "../../data/recorder";
import type { HomeAssistant } from "../../types";
import "./ha-chart-base";
import type {
ChartResizeOptions,
ChartDatasetExtra,
HaChartBase,
} from "./ha-chart-base";
import type { ChartResizeOptions, HaChartBase } from "./ha-chart-base";
export const supportedStatTypeMap: Record<StatisticType, StatisticType> = {
mean: "mean",
@@ -83,14 +79,10 @@ export class StatisticsChart extends LitElement {
@state() private _chartData: ChartData = { datasets: [] };
@state() private _chartDatasetExtra: ChartDatasetExtra[] = [];
@state() private _statisticIds: string[] = [];
@state() private _chartOptions?: ChartOptions;
@state() private _hiddenStats = new Set<string>();
@query("ha-chart-base") private _chart?: HaChartBase;
private _computedStyle?: CSSStyleDeclaration;
@@ -104,9 +96,6 @@ export class StatisticsChart extends LitElement {
}
public willUpdate(changedProps: PropertyValues) {
if (changedProps.has("legendMode")) {
this._hiddenStats.clear();
}
if (
!this.hasUpdated ||
changedProps.has("unit") ||
@@ -121,8 +110,7 @@ export class StatisticsChart extends LitElement {
changedProps.has("statisticsData") ||
changedProps.has("statTypes") ||
changedProps.has("chartType") ||
changedProps.has("hideLegend") ||
changedProps.has("_hiddenStats")
changedProps.has("hideLegend")
) {
this._generateData();
}
@@ -157,30 +145,14 @@ export class StatisticsChart extends LitElement {
return html`
<ha-chart-base
externalHidden
.hass=${this.hass}
.data=${this._chartData}
.extraData=${this._chartDatasetExtra}
.options=${this._chartOptions}
.chartType=${this.chartType}
@dataset-hidden=${this._datasetHidden}
@dataset-unhidden=${this._datasetUnhidden}
></ha-chart-base>
`;
}
private _datasetHidden(ev) {
ev.stopPropagation();
this._hiddenStats.add(this._statisticIds[ev.detail.index]);
this.requestUpdate("_hiddenStats");
}
private _datasetUnhidden(ev) {
ev.stopPropagation();
this._hiddenStats.delete(this._statisticIds[ev.detail.index]);
this.requestUpdate("_hiddenStats");
}
private _createOptions(unit?: string) {
this._chartOptions = {
parsing: false,
@@ -302,7 +274,6 @@ export class StatisticsChart extends LitElement {
let colorIndex = 0;
const statisticsData = Object.entries(this.statisticsData);
const totalDataSets: ChartDataset<"line">[] = [];
const totalDatasetExtras: ChartDatasetExtra[] = [];
const statisticIds: string[] = [];
let endTime: Date;
@@ -353,7 +324,6 @@ export class StatisticsChart extends LitElement {
// The datasets for the current statistic
const statDataSets: ChartDataset<"line">[] = [];
const statDatasetExtras: ChartDatasetExtra[] = [];
const pushData = (
start: Date,
@@ -414,20 +384,9 @@ export class StatisticsChart extends LitElement {
})
: this.statTypes;
let displayed_legend = false;
sortedTypes.forEach((type) => {
if (statisticsHaveType(stats, type)) {
const band = drawBands && (type === "min" || type === "max");
if (!this.hideLegend) {
const show_legend = hasMean
? type === "mean"
: displayed_legend === false;
statDatasetExtras.push({
legend_label: name,
show_legend,
});
displayed_legend = displayed_legend || show_legend;
}
statTypes.push(type);
statDataSets.push({
label: name
@@ -449,9 +408,6 @@ export class StatisticsChart extends LitElement {
band && hasMean ? color + (this.hideLegend ? "00" : "7F") : color,
backgroundColor: band ? color + "3F" : color + "7F",
pointRadius: 0,
hidden: !this.hideLegend
? this._hiddenStats.has(statistic_id)
: false,
data: [],
// @ts-ignore
unit: meta?.unit_of_measurement,
@@ -490,7 +446,6 @@ export class StatisticsChart extends LitElement {
// Concat two arrays
Array.prototype.push.apply(totalDataSets, statDataSets);
Array.prototype.push.apply(totalDatasetExtras, statDatasetExtras);
});
if (unit) {
@@ -500,7 +455,6 @@ export class StatisticsChart extends LitElement {
this._chartData = {
datasets: totalDataSets,
};
this._chartDatasetExtra = totalDatasetExtras;
this._statisticIds = statisticIds;
}

View File

@@ -205,9 +205,7 @@ export class TimelineController extends BarController {
const y = vScale.getPixelForValue(this.index);
const xStart = iScale.getPixelForValue(
Math.max(iScale.min, data.start.getTime())
);
const xStart = iScale.getPixelForValue(data.start.getTime());
const xEnd = iScale.getPixelForValue(data.end.getTime());
const width = xEnd - xStart;

View File

@@ -49,7 +49,7 @@ export class TimeLineScale extends TimeScale {
max = isFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit);
// Make sure that max is strictly higher than min (required by the lookup table)
this.min = adapter.parse(options.min, this) ?? Math.min(min, max - 1);
this.max = adapter.parse(options.max, this) ?? Math.max(min + 1, max);
this.min = Math.min(min, max - 1);
this.max = Math.max(min + 1, max);
}
}

View File

@@ -44,8 +44,6 @@ class HaDataTableIcon extends LitElement {
div {
position: absolute;
right: 28px;
inset-inline-end: 28px;
inset-inline-start: initial;
z-index: 1002;
outline: none;
font-size: 10px;

View File

@@ -688,12 +688,15 @@ export class HaDataTable extends LitElement {
padding-left: 16px;
/* @noflip */
padding-right: 0;
/* @noflip */
padding-inline-start: 16px;
/* @noflip */
padding-inline-end: initial;
width: 60px;
}
:host([dir="rtl"]) .mdc-data-table__header-cell--checkbox,
:host([dir="rtl"]) .mdc-data-table__cell--checkbox {
/* @noflip */
padding-left: 0;
/* @noflip */
padding-right: 16px;
}
.mdc-data-table__table {
height: 100%;
@@ -720,7 +723,11 @@ export class HaDataTable extends LitElement {
}
.mdc-data-table__cell--numeric {
text-align: var(--float-end);
text-align: right;
}
:host([dir="rtl"]) .mdc-data-table__cell--numeric {
/* @noflip */
text-align: left;
}
.mdc-data-table__cell--icon {
@@ -746,7 +753,15 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:not(
.not-sorted
) {
text-align: var(--float-start);
text-align: left;
}
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:hover,
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:not(
.not-sorted
) {
text-align: right;
}
.mdc-data-table__cell--icon:first-child img,
@@ -756,14 +771,27 @@ export class HaDataTable extends LitElement {
.mdc-data-table__cell--icon:first-child ha-domain-icon,
.mdc-data-table__cell--icon:first-child ha-service-icon {
margin-left: 8px;
margin-inline-start: 8px;
margin-inline-end: initial;
}
:host([dir="rtl"]) .mdc-data-table__cell--icon:first-child ha-icon,
:host([dir="rtl"])
.mdc-data-table__cell--icon:first-child
ha-state-icon,
:host([dir="rtl"])
.mdc-data-table__cell--icon:first-child
ha-svg-icon
:host([dir="rtl"])
.mdc-data-table__cell--icon:first-child
img {
margin-left: auto;
margin-right: 8px;
}
.mdc-data-table__cell--icon:first-child state-badge {
margin-right: -8px;
margin-inline-end: -8px;
margin-inline-start: initial;
}
:host([dir="rtl"]) .mdc-data-table__cell--icon:first-child state-badge {
margin-right: auto;
margin-left: -8px;
}
.mdc-data-table__cell--overflow-menu,
@@ -796,8 +824,15 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell--icon-button:first-child,
.mdc-data-table__cell--icon-button:first-child {
padding-left: 16px;
padding-inline-start: 16px;
padding-inline-end: initial;
}
:host([dir="rtl"])
.mdc-data-table__header-cell--overflow-menu:first-child,
:host([dir="rtl"]) .mdc-data-table__cell--overflow-menu:first-child,
:host([dir="rtl"])
.mdc-data-table__header-cell--overflow-menu:first-child,
:host([dir="rtl"]) .mdc-data-table__cell--overflow-menu:first-child {
padding-left: 8px;
padding-right: 16px;
}
.mdc-data-table__cell--overflow-menu:last-child,
@@ -805,8 +840,14 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell--icon-button:last-child,
.mdc-data-table__cell--icon-button:last-child {
padding-right: 16px;
padding-inline-end: 16px;
padding-inline-start: initial;
}
:host([dir="rtl"])
.mdc-data-table__header-cell--overflow-menu:last-child,
:host([dir="rtl"]) .mdc-data-table__cell--overflow-menu:last-child,
:host([dir="rtl"]) .mdc-data-table__header-cell--icon-button:last-child,
:host([dir="rtl"]) .mdc-data-table__cell--icon-button:last-child {
padding-right: 8px;
padding-left: 16px;
}
.mdc-data-table__cell--overflow-menu,
.mdc-data-table__header-cell--overflow-menu {
@@ -826,15 +867,28 @@ export class HaDataTable extends LitElement {
letter-spacing: 0.0071428571em;
text-decoration: inherit;
text-transform: inherit;
text-align: var(--float-start);
text-align: left;
}
:host([dir="rtl"]) .mdc-data-table__header-cell {
/* @noflip */
text-align: right;
}
.mdc-data-table__header-cell--numeric {
text-align: var(--float-end);
text-align: right;
}
.mdc-data-table__header-cell--numeric.sortable:hover,
.mdc-data-table__header-cell--numeric.sortable:not(.not-sorted) {
text-align: var(--float-start);
text-align: left;
}
:host([dir="rtl"]) .mdc-data-table__header-cell--numeric {
/* @noflip */
text-align: left;
}
:host([dir="rtl"]) .mdc-data-table__header-cell--numeric.sortable:hover,
:host([dir="rtl"])
.mdc-data-table__header-cell--numeric.sortable:not(.not-sorted) {
text-align: right;
}
/* custom from here */
@@ -855,15 +909,20 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell span {
position: relative;
left: 0px;
inset-inline-start: 0px;
inset-inline-end: initial;
}
:host([dir="rtl"]) .mdc-data-table__header-cell span {
left: auto;
right: 0px;
}
.mdc-data-table__header-cell.sortable {
cursor: pointer;
}
.mdc-data-table__header-cell > * {
transition: var(--float-start) 0.2s ease;
transition: left 0.2s ease;
}
:host([dir="rtl"]) .mdc-data-table__header-cell > * {
transition: right 0.2s ease;
}
.mdc-data-table__header-cell ha-svg-icon {
top: -3px;
@@ -871,20 +930,35 @@ export class HaDataTable extends LitElement {
}
.mdc-data-table__header-cell.not-sorted ha-svg-icon {
left: -20px;
inset-inline-start: -20px;
inset-inline-end: initial;
}
:host([dir="rtl"]) .mdc-data-table__header-cell.not-sorted ha-svg-icon {
right: -20px;
}
.mdc-data-table__header-cell.sortable:not(.not-sorted) span,
.mdc-data-table__header-cell.sortable.not-sorted:hover span {
left: 24px;
inset-inline-start: 24px;
inset-inline-end: initial;
}
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable:not(.not-sorted)
span,
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable.not-sorted:hover
span {
left: auto;
right: 24px;
}
.mdc-data-table__header-cell.sortable:not(.not-sorted) ha-svg-icon,
.mdc-data-table__header-cell.sortable:hover.not-sorted ha-svg-icon {
left: 12px;
inset-inline-start: 12px;
inset-inline-end: initial;
}
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable:not(.not-sorted)
ha-svg-icon,
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable:hover.not-sorted
ha-svg-icon {
left: auto;
right: 12px;
}
.table-header {
border-bottom: 1px solid var(--divider-color);
@@ -892,8 +966,6 @@ export class HaDataTable extends LitElement {
search-input {
display: block;
flex: 1;
--mdc-text-field-fill-color: var(--sidebar-background-color);
--mdc-text-field-idle-line-color: transparent;
}
slot[name="header"] {
display: block;

View File

@@ -9,7 +9,6 @@ import {
localizeWeekdays,
localizeMonths,
} from "../common/datetime/localize_date";
import { mainWindow } from "../common/dom/get_main_window";
// Set the current date to the left picker instead of the right picker because the right is hidden
const CustomDateRangePicker = Vue.extend({
@@ -158,7 +157,7 @@ class DateRangePickerElement extends WrappedElement {
min-width: initial !important;
max-height: var(--date-range-picker-max-height);
overflow-y: auto;
}
}
.daterangepicker:before {
display: none;
}
@@ -268,37 +267,15 @@ class DateRangePickerElement extends WrappedElement {
.calendar-table {
padding: 0 !important;
}
.calendar-time {
direction: ltr;
}
.daterangepicker.ltr {
direction: var(--direction);
text-align: var(--float-start);
direction: ltr;
text-align: left;
}
.vue-daterange-picker{
min-width: unset !important;
display: block !important;
}
`;
if (mainWindow.document.dir === "rtl") {
style.innerHTML += `
.daterangepicker .calendar-table .next span {
transform: rotate(135deg);
-webkit-transform: rotate(135deg);
}
.daterangepicker .calendar-table .prev span {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
}
.daterangepicker td.start-date {
border-radius: 0 50% 50% 0;
}
.daterangepicker td.end-date {
border-radius: 50% 0 0 50%;
}
`;
}
const shadowRoot = this.shadowRoot!;
shadowRoot.appendChild(style);
// Stop click events from reaching the document, otherwise it will close the picker immediately.

View File

@@ -133,9 +133,9 @@ export class HaStateLabelBadge extends LitElement {
entityState,
this._timerTimeRemaining
)}
.description=${this.showName
? this.name ?? computeStateName(entityState)
: undefined}
.description=${this.showName === false
? undefined
: this.name ?? computeStateName(entityState)}
>
${!image && showIcon
? html`<ha-state-icon

View File

@@ -32,9 +32,7 @@ export class StateBadge extends LitElement {
@property() public overrideImage?: string;
// Cannot be a boolean attribute because undefined is treated different than
// false. When it is undefined, state is still colored for light entities.
@property({ attribute: false }) public stateColor?: boolean;
@property({ type: Boolean }) public stateColor = false;
@property() public color?: string;
@@ -72,7 +70,7 @@ export class StateBadge extends LitElement {
const domain = this.stateObj
? computeStateDomain(this.stateObj)
: undefined;
return this.stateColor ?? domain === "light";
return this.stateColor || (domain === "light" && this.stateColor !== false);
}
protected render() {

View File

@@ -3,6 +3,7 @@ import type { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { computeStateName } from "../../common/entity/compute_state_name";
import { computeRTL } from "../../common/util/compute_rtl";
import type { HomeAssistant } from "../../types";
import "../ha-relative-time";
import "./state-badge";
@@ -15,6 +16,9 @@ class StateInfo extends LitElement {
@property({ type: Boolean }) public inDialog = false;
// property used only in CSS
@property({ type: Boolean, reflect: true }) public rtl = false;
@property() public color?: string;
protected render() {
@@ -75,6 +79,18 @@ class StateInfo extends LitElement {
</div>`;
}
protected updated(changedProps) {
super.updated(changedProps);
if (!changedProps.has("hass")) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass.locale) {
this.rtl = computeRTL(this.hass);
}
}
static get styles(): CSSResultGroup {
return css`
:host {
@@ -90,14 +106,17 @@ class StateInfo extends LitElement {
.info {
margin-left: 8px;
margin-inline-start: 8px;
margin-inline-end: initial;
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
min-width: 0;
text-align: var(--float-start);
}
:host([rtl]) .info {
margin-right: 8px;
margin-left: 0;
text-align: right;
}
.name {

View File

@@ -1,6 +1,6 @@
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
import { HassEntity } from "home-assistant-js-websocket";
import { html, LitElement, nothing, PropertyValues, TemplateResult } from "lit";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
@@ -36,12 +36,8 @@ type ScorableAreaRegistryEntry = ScorableTextItem & AreaRegistryEntry;
const rowRenderer: ComboBoxLitRenderer<AreaRegistryEntry> = (item) =>
html`<ha-list-item
graphic="icon"
class=${classMap({ "add-new": item.area_id === "add_new" })}
>
${item.icon
? html`<ha-icon slot="graphic" .icon=${item.icon}></ha-icon>`
: nothing}
${item.name}
</ha-list-item>`;
@@ -139,7 +135,6 @@ export class HaAreaPicker extends LitElement {
area_id: "no_areas",
name: this.hass.localize("ui.components.area-picker.no_areas"),
picture: null,
icon: null,
aliases: [],
},
];
@@ -267,9 +262,7 @@ export class HaAreaPicker extends LitElement {
}
if (areaIds) {
outputAreas = outputAreas.filter((area) =>
areaIds!.includes(area.area_id)
);
outputAreas = areas.filter((area) => areaIds!.includes(area.area_id));
}
if (excludeAreas) {
@@ -284,7 +277,6 @@ export class HaAreaPicker extends LitElement {
area_id: "no_areas",
name: this.hass.localize("ui.components.area-picker.no_match"),
picture: null,
icon: null,
aliases: [],
},
];
@@ -298,7 +290,6 @@ export class HaAreaPicker extends LitElement {
area_id: "add_new",
name: this.hass.localize("ui.components.area-picker.add_new"),
picture: null,
icon: "mdi:plus",
aliases: [],
},
];

View File

@@ -69,7 +69,6 @@ export class HaButtonToggleGroup extends LitElement {
display: flex;
--mdc-icon-button-size: var(--button-toggle-size, 36px);
--mdc-icon-size: var(--button-toggle-icon-size, 20px);
direction: ltr;
}
mwc-button {
--mdc-shape-small: 0;
@@ -120,6 +119,19 @@ export class HaButtonToggleGroup extends LitElement {
--mdc-shape-small: 4px;
border-right-width: 1px;
}
:host([dir="rtl"]) ha-icon-button:first-child,
:host([dir="rtl"]) mwc-button:first-child {
border-radius: 0 4px 4px 0;
border-right-width: 1px;
--mdc-shape-small: 0 4px 4px 0;
--mdc-button-outline-width: 1px;
}
:host([dir="rtl"]) ha-icon-button:last-child,
:host([dir="rtl"]) mwc-button:last-child {
--mdc-shape-small: 4px 0 0 4px;
border-radius: 4px 0 0 4px;
}
`;
}
}

View File

@@ -156,7 +156,6 @@ class HaClimateState extends LitElement {
.current {
color: var(--secondary-text-color);
direction: var(--direction);
}
.state-label {

View File

@@ -127,11 +127,9 @@ export class HaControlButton extends LitElement {
opacity 180ms ease-in-out;
opacity: var(--control-button-background-opacity);
}
.button {
.button ::slotted(*) {
transition: color 180ms ease-in-out;
color: var(--control-button-icon-color);
}
.button ::slotted(*) {
pointer-events: none;
}
.button:disabled {

View File

@@ -273,13 +273,9 @@ export class HaControlNumberButton extends LitElement {
}
.button.minus {
left: 0;
inset-inline-start: 0;
inset-inline-end: initial;
}
.button.plus {
right: 0;
inset-inline-start: initial;
inset-inline-end: 0;
}
.unit {
white-space: pre;

View File

@@ -32,6 +32,7 @@ import { firstWeekdayIndex } from "../common/datetime/first_weekday";
import { formatDate } from "../common/datetime/format_date";
import { formatDateTime } from "../common/datetime/format_date_time";
import { useAmPm } from "../common/datetime/use_am_pm";
import { computeRTLDirection } from "../common/util/compute_rtl";
import { HomeAssistant } from "../types";
import "./date-range-picker";
import "./ha-icon-button";
@@ -64,6 +65,8 @@ export class HaDateRangePicker extends LitElement {
@state() private _hour24format = false;
@state() private _rtlDirection = "ltr";
@property({ type: Boolean }) public extendedPresets = false;
@property() public openingDirection?: "right" | "left" | "center" | "inline";
@@ -233,6 +236,7 @@ export class HaDateRangePicker extends LitElement {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass.locale) {
this._hour24format = !useAmPm(this.hass.locale);
this._rtlDirection = computeRTLDirection(this.hass);
}
}
}
@@ -302,7 +306,11 @@ export class HaDateRangePicker extends LitElement {
></ha-icon-button>`}
</div>
${this.ranges !== false && (this.ranges || this._ranges)
? html`<div slot="ranges" class="date-range-ranges">
? html`<div
slot="ranges"
class="date-range-ranges"
.dir=${this._rtlDirection}
>
<mwc-list @action=${this._setDateRange} activatable>
${Object.keys(this.ranges || this._ranges!).map(
(name) => html`<mwc-list-item>${name}</mwc-list-item>`

View File

@@ -12,8 +12,6 @@ export class HaDrawer extends DrawerBase {
private _mc?: HammerManager;
private _rtlStyle?: HTMLElement;
protected createAdapter() {
return {
...super.createAdapter(),
@@ -34,26 +32,7 @@ export class HaDrawer extends DrawerBase {
super.updated(changedProps);
if (changedProps.has("direction")) {
this.mdcRoot.dir = this.direction;
if (this.direction === "rtl") {
this._rtlStyle = document.createElement("style");
this._rtlStyle.innerHTML = `
.mdc-drawer--animate {
transform: translateX(100%);
}
.mdc-drawer--opening {
transform: translateX(0);
}
.mdc-drawer--closing {
transform: translateX(100%);
}
`;
this.shadowRoot!.appendChild(this._rtlStyle);
} else if (this._rtlStyle) {
this.shadowRoot!.removeChild(this._rtlStyle);
}
}
if (changedProps.has("open") && this.open && this.type === "modal") {
this._setupSwipe();
} else if (this._mc) {
@@ -87,8 +66,6 @@ export class HaDrawer extends DrawerBase {
position: fixed;
top: 0;
border-color: var(--divider-color, rgba(0, 0, 0, 0.12));
inset-inline-start: 0 !important;
inset-inline-end: initial !important;
}
.mdc-drawer.mdc-drawer--modal.mdc-drawer--open {
z-index: 200;

View File

@@ -54,8 +54,7 @@ export const computeInitialHaFormData = (
"icon" in selector ||
"template" in selector ||
"text" in selector ||
"theme" in selector ||
"object" in selector
"theme" in selector
) {
data[field.name] = "";
} else if ("number" in selector) {

View File

@@ -1,13 +1,11 @@
import { FormfieldBase } from "@material/mwc-formfield/mwc-formfield-base";
import { styles } from "@material/mwc-formfield/mwc-formfield.css";
import { css } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
@customElement("ha-formfield")
export class HaFormfield extends FormfieldBase {
@property({ type: Boolean, reflect: true }) public disabled = false;
protected _labelClick() {
const input = this.input as HTMLInputElement | undefined;
if (!input) return;
@@ -46,9 +44,6 @@ export class HaFormfield extends FormfieldBase {
padding-inline-start: 4px;
padding-inline-end: 0;
}
:host([disabled]) label {
color: var(--disabled-text-color);
}
`,
];
}

View File

@@ -136,8 +136,6 @@ class HaMenuButton extends LitElement {
height: 12px;
top: 9px;
right: 7px;
inset-inline-end: 7px;
inset-inline-start: initial;
border-radius: 50%;
border: 2px solid var(--app-header-background-color);
}

View File

@@ -19,9 +19,7 @@ class HaMetric extends LitElement {
<ha-settings-row>
<span slot="heading"> ${this.heading} </span>
<div slot="description" .title=${this.tooltip ?? ""}>
<span class="value">
<div>${roundedValue} %</div>
</span>
<span class="value"> ${roundedValue} % </span>
<ha-bar
class=${classMap({
"target-warning": roundedValue > 50,
@@ -72,10 +70,6 @@ class HaMetric extends LitElement {
padding-inline-start: initial;
flex-shrink: 0;
}
.value > div {
direction: ltr;
text-align: var(--float-start);
}
`;
}
}

View File

@@ -2,7 +2,6 @@ import { LitElement, PropertyValues, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import QRCode from "qrcode";
import "./ha-alert";
import { rgb2hex } from "../common/color/convert-color";
@customElement("ha-qr-code")
export class HaQrCode extends LitElement {
@@ -66,37 +65,16 @@ export class HaQrCode extends LitElement {
changedProperties.has("centerImage"))
) {
const computedStyles = getComputedStyle(this);
const textRgb = computedStyles.getPropertyValue(
"--rgb-primary-text-color"
);
const backgroundRgb = computedStyles.getPropertyValue(
"--rgb-card-background-color"
);
const textHex = rgb2hex(
textRgb.split(",").map((a) => parseInt(a, 10)) as [
number,
number,
number,
]
);
const backgroundHex = rgb2hex(
backgroundRgb.split(",").map((a) => parseInt(a, 10)) as [
number,
number,
number,
]
);
QRCode.toCanvas(canvas, this.data, {
errorCorrectionLevel:
this.errorCorrectionLevel || (this.centerImage ? "Q" : "M"),
errorCorrectionLevel: this.errorCorrectionLevel,
width: this.width,
scale: this.scale,
margin: this.margin,
maskPattern: this.maskPattern,
color: {
light: backgroundHex,
dark: textHex,
light: computedStyles.getPropertyValue("--card-background-color"),
dark: computedStyles.getPropertyValue("--primary-text-color"),
},
}).catch((err) => {
this._error = err.message;

View File

@@ -186,8 +186,6 @@ class HaQrScanner extends LitElement {
position: absolute;
bottom: 8px;
right: 8px;
inset-inline-end: 8px;
inset-inline-start: initial;
background: #727272b2;
color: white;
border-radius: 50%;

View File

@@ -1,30 +0,0 @@
import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import { QRCodeSelector } from "../../data/selector";
import "../ha-qr-code";
@customElement("ha-selector-qr_code")
export class HaSelectorQRCode extends LitElement {
@property({ attribute: false }) public selector!: QRCodeSelector;
protected render() {
return html`<ha-qr-code
.data=${this.selector.qr_code?.data}
.scale=${this.selector.qr_code?.scale}
.errorCorrectionLevel=${this.selector.qr_code?.error_correction_level}
.centerImage=${this.selector.qr_code?.center_image}
></ha-qr-code>`;
}
static styles = css`
ha-qr-code {
text-align: center;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-selector-qr_code": HaSelectorQRCode;
}
}

View File

@@ -102,10 +102,7 @@ export class HaSelectSelector extends LitElement {
${this.label}
${options.map(
(item: SelectOption) => html`
<ha-formfield
.label=${item.label}
.disabled=${item.disabled || this.disabled}
>
<ha-formfield .label=${item.label}>
<ha-radio
.checked=${item.value === this.value}
.value=${item.value}

View File

@@ -34,7 +34,6 @@ const LOAD_ELEMENTS = {
navigation: () => import("./ha-selector-navigation"),
number: () => import("./ha-selector-number"),
object: () => import("./ha-selector-object"),
qr_code: () => import("./ha-selector-qr-code"),
select: () => import("./ha-selector-select"),
selector: () => import("./ha-selector-selector"),
state: () => import("./ha-selector-state"),

View File

@@ -93,8 +93,6 @@ export class HaServiceControl extends LitElement {
@property({ type: Boolean, reflect: true }) public hidePicker = false;
@property({ type: Boolean }) public hideDescription = false;
@state() private _value!: this["value"];
@state() private _checkedKeys = new Set();
@@ -375,8 +373,7 @@ export class HaServiceControl extends LitElement {
)) ||
serviceData?.description;
return html`
${this.hidePicker
return html`${this.hidePicker
? nothing
: html`<ha-service-picker
.hass=${this.hass}
@@ -384,33 +381,29 @@ export class HaServiceControl extends LitElement {
.disabled=${this.disabled}
@value-changed=${this._serviceChanged}
></ha-service-picker>`}
${this.hideDescription
? nothing
: html`
<div class="description">
${description ? html`<p>${description}</p>` : ""}
${this._manifest
? html` <a
href=${this._manifest.is_built_in
? documentationUrl(
this.hass,
`/integrations/${this._manifest.domain}`
)
: this._manifest.documentation}
title=${this.hass.localize(
"ui.components.service-control.integration_doc"
)}
target="_blank"
rel="noreferrer"
>
<ha-icon-button
.path=${mdiHelpCircle}
class="help-icon"
></ha-icon-button>
</a>`
: nothing}
</div>
`}
<div class="description">
${description ? html`<p>${description}</p>` : ""}
${this._manifest
? html` <a
href=${this._manifest.is_built_in
? documentationUrl(
this.hass,
`/integrations/${this._manifest.domain}`
)
: this._manifest.documentation}
title=${this.hass.localize(
"ui.components.service-control.integration_doc"
)}
target="_blank"
rel="noreferrer"
>
<ha-icon-button
.path=${mdiHelpCircle}
class="help-icon"
></ha-icon-button>
</a>`
: ""}
</div>
${serviceData && "target" in serviceData
? html`<ha-settings-row .narrow=${this.narrow}>
${hasOptional
@@ -524,8 +517,7 @@ export class HaServiceControl extends LitElement {
></ha-selector>
</ha-settings-row>`
: "";
})}
`;
})}`;
}
private _localizeValueCallback = (key: string) => {

View File

@@ -114,14 +114,11 @@ class HaServicePicker extends LitElement {
if (!filter) {
return processedServices;
}
const split_filter = filter.split(" ");
return processedServices.filter((service) => {
const lower_service_name = service.name.toLowerCase();
const lower_service = service.service.toLowerCase();
return split_filter.every(
(f) => lower_service_name.includes(f) || lower_service.includes(f)
);
});
return processedServices.filter(
(service) =>
service.service.toLowerCase().includes(filter) ||
service.name?.toLowerCase().includes(filter)
);
}
);

View File

@@ -38,6 +38,7 @@ import { storage } from "../common/decorators/storage";
import { fireEvent } from "../common/dom/fire_event";
import { toggleAttribute } from "../common/dom/toggle_attribute";
import { stringCompare } from "../common/string/compare";
import { computeRTL } from "../common/util/compute_rtl";
import { throttle } from "../common/util/throttle";
import { ActionHandlerDetail } from "../data/lovelace/action_handler";
import {
@@ -306,12 +307,16 @@ class HaSidebar extends SubscribeMixin(LitElement) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass.locale) {
toggleAttribute(this, "rtl", computeRTL(this.hass));
}
this._calculateCounts();
if (!SUPPORT_SCROLL_IF_NEEDED) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.panelUrl !== this.hass.panelUrl) {
const selectedEl = this.shadowRoot!.querySelector(".iron-selected");
if (selectedEl) {
@@ -846,22 +851,29 @@ class HaSidebar extends SubscribeMixin(LitElement) {
font-size: 20px;
align-items: center;
padding-left: calc(4px + env(safe-area-inset-left));
padding-inline-start: calc(4px + env(safe-area-inset-left));
padding-inline-end: initial;
}
:host([rtl]) .menu {
padding-left: 4px;
padding-right: calc(4px + env(safe-area-inset-right));
}
:host([expanded]) .menu {
width: calc(256px + env(safe-area-inset-left));
}
:host([rtl][expanded]) .menu {
width: calc(256px + env(safe-area-inset-right));
}
.menu ha-icon-button {
color: var(--sidebar-icon-color);
}
.title {
margin-left: 19px;
margin-inline-start: 19px;
margin-inline-end: initial;
width: 100%;
display: none;
}
:host([rtl]) .title {
margin-left: 0;
margin-right: 19px;
}
:host([narrow]) .title {
margin: 0;
padding: 0 16px;
@@ -892,8 +904,11 @@ class HaSidebar extends SubscribeMixin(LitElement) {
overflow-x: hidden;
background: none;
margin-left: env(safe-area-inset-left);
margin-inline-start: env(safe-area-inset-left);
margin-inline-end: initial;
}
:host([rtl]) paper-listbox {
margin-left: initial;
margin-right: env(safe-area-inset-right);
}
a {
@@ -910,8 +925,6 @@ class HaSidebar extends SubscribeMixin(LitElement) {
box-sizing: border-box;
margin: 4px;
padding-left: 12px;
padding-inline-start: 12px;
padding-inline-end: initial;
border-radius: 4px;
--paper-item-min-height: 40px;
width: 48px;
@@ -919,6 +932,10 @@ class HaSidebar extends SubscribeMixin(LitElement) {
:host([expanded]) paper-icon-item {
width: 248px;
}
:host([rtl]) paper-icon-item {
padding-left: auto;
padding-right: 12px;
}
ha-icon[slot="item-icon"],
ha-svg-icon[slot="item-icon"] {
@@ -993,8 +1010,11 @@ class HaSidebar extends SubscribeMixin(LitElement) {
.configuration-container {
display: flex;
margin-left: env(safe-area-inset-left);
margin-inline-start: env(safe-area-inset-left);
margin-inline-end: initial;
}
:host([rtl]) .notifications-container,
:host([rtl]) .configuration-container {
margin-left: initial;
margin-right: env(safe-area-inset-right);
}
.notifications {
cursor: pointer;
@@ -1005,18 +1025,23 @@ class HaSidebar extends SubscribeMixin(LitElement) {
}
.profile {
margin-left: env(safe-area-inset-left);
margin-inline-start: env(safe-area-inset-left);
margin-inline-end: initial;
}
:host([rtl]) .profile {
margin-left: initial;
margin-right: env(safe-area-inset-right);
}
.profile paper-icon-item {
padding-left: 4px;
padding-inline-start: 4px;
padding-inline-end: auto;
}
:host([rtl]) .profile paper-icon-item {
padding-left: auto;
padding-right: 4px;
}
.profile .item-text {
margin-left: 8px;
margin-inline-start: 8px;
margin-inline-end: initial;
}
:host([rtl]) .profile .item-text {
margin-right: 8px;
}
.notification-badge,
@@ -1040,8 +1065,6 @@ class HaSidebar extends SubscribeMixin(LitElement) {
position: absolute;
bottom: 14px;
left: 26px;
inset-inline-start: 26px;
inset-inline-end: initial;
font-size: 0.65em;
}
@@ -1083,9 +1106,9 @@ class HaSidebar extends SubscribeMixin(LitElement) {
font-weight: 500;
}
.menu ha-icon-button {
-webkit-transform: scaleX(var(--scale-direction));
transform: scaleX(var(--scale-direction));
:host([rtl]) .menu ha-icon-button {
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
`,
];

View File

@@ -2,15 +2,9 @@ import { customElement } from "lit/decorators";
import "element-internals-polyfill";
import { MdSlider } from "@material/web/slider/slider";
import { CSSResult, css } from "lit";
import { mainWindow } from "../common/dom/get_main_window";
@customElement("ha-slider")
export class HaSlider extends MdSlider {
public connectedCallback() {
super.connectedCallback();
this.dir = mainWindow.document.dir;
}
static override styles: CSSResult[] = [
...MdSlider.styles,
css`

View File

@@ -14,16 +14,9 @@ declare global {
oldPath?: ItemPath;
newPath?: ItemPath;
};
"drag-start": undefined;
"drag-end": undefined;
}
}
export type HaSortableOptions = Omit<
SortableInstance.SortableOptions,
"onStart" | "onChoose" | "onEnd"
>;
@customElement("ha-sortable")
export class HaSortable extends LitElement {
private _sortable?: SortableInstance;
@@ -43,17 +36,8 @@ export class HaSortable extends LitElement {
@property({ type: String, attribute: "handle-selector" })
public handleSelector?: string;
@property({ type: String })
public group?: string | SortableInstance.GroupOptions;
@property({ type: Boolean, attribute: "invert-swap" })
public invertSwap: boolean = false;
@property({ attribute: false })
public options?: HaSortableOptions;
@property({ type: Boolean })
public rollback: boolean = true;
@property({ type: String, attribute: "group" })
public group?: string;
protected updated(changedProperties: PropertyValues<this>) {
if (changedProperties.has("disabled")) {
@@ -82,9 +66,6 @@ export class HaSortable extends LitElement {
public connectedCallback() {
super.connectedCallback();
this._shouldBeDestroy = false;
if (this.hasUpdated) {
this.requestUpdate();
}
}
protected createRenderRoot() {
@@ -100,10 +81,11 @@ export class HaSortable extends LitElement {
}
.sortable-ghost {
box-shadow: 0 0 0 2px var(--primary-color);
border: 2px solid var(--primary-color);
background: rgba(var(--rgb-primary-color), 0.25);
border-radius: 4px;
opacity: 0.4;
pointer-events: none;
}
.sortable-drag {
@@ -127,9 +109,8 @@ export class HaSortable extends LitElement {
const options: SortableInstance.Options = {
animation: 150,
...this.options,
swapThreshold: 0.75,
onChoose: this._handleChoose,
onStart: this._handleStart,
onEnd: this._handleEnd,
};
@@ -139,8 +120,8 @@ export class HaSortable extends LitElement {
if (this.handleSelector) {
options.handle = this.handleSelector;
}
if (this.invertSwap !== undefined) {
options.invertSwap = this.invertSwap;
if (this.draggableSelector) {
options.draggable = this.draggableSelector;
}
if (this.group) {
options.group = this.group;
@@ -150,9 +131,8 @@ export class HaSortable extends LitElement {
}
private _handleEnd = async (evt: SortableEvent) => {
fireEvent(this, "drag-end");
// put back in original location
if (this.rollback && (evt.item as any).placeholder) {
if ((evt.item as any).placeholder) {
(evt.item as any).placeholder.replaceWith(evt.item);
delete (evt.item as any).placeholder;
}
@@ -178,12 +158,7 @@ export class HaSortable extends LitElement {
});
};
private _handleStart = () => {
fireEvent(this, "drag-start");
};
private _handleChoose = (evt: SortableEvent) => {
if (!this.rollback) return;
(evt.item as any).placeholder = document.createComment("sort-placeholder");
evt.item.after((evt.item as any).placeholder);
};

View File

@@ -98,7 +98,6 @@ export class HaTargetPicker extends LitElement {
area_id,
area?.name || area_id,
undefined,
area?.icon,
mdiSofa
);
})
@@ -111,7 +110,6 @@ export class HaTargetPicker extends LitElement {
device_id,
device ? computeDeviceName(device, this.hass) : device_id,
undefined,
undefined,
mdiDevices
);
})
@@ -211,8 +209,7 @@ export class HaTargetPicker extends LitElement {
id: string,
name: string,
entityState?: HassEntity,
icon?: string | null,
fallbackIconPath?: string
iconPath?: string
) {
return html`
<div
@@ -220,17 +217,12 @@ export class HaTargetPicker extends LitElement {
[type]: true,
})}"
>
${icon
? html`<ha-icon
${iconPath
? html`<ha-svg-icon
class="mdc-chip__icon mdc-chip__icon--leading"
.icon=${icon}
></ha-icon>`
: fallbackIconPath
? html`<ha-svg-icon
class="mdc-chip__icon mdc-chip__icon--leading"
.path=${fallbackIconPath}
></ha-svg-icon>`
: ""}
.path=${iconPath}
></ha-svg-icon>`
: ""}
${entityState
? html`<ha-state-icon
class="mdc-chip__icon mdc-chip__icon--leading"

View File

@@ -3,11 +3,18 @@ import { styles as textfieldStyles } from "@material/mwc-textfield/mwc-textfield
import { styles as textareaStyles } from "@material/mwc-textarea/mwc-textarea.css";
import { css, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
import { mainWindow } from "../common/dom/get_main_window";
@customElement("ha-textarea")
export class HaTextArea extends TextAreaBase {
@property({ type: Boolean, reflect: true }) autogrow = false;
firstUpdated() {
super.firstUpdated();
this.setAttribute("dir", mainWindow.document.dir);
}
updated(changedProperties: PropertyValues) {
super.updated(changedProperties);
if (this.autogrow && changedProperties.has("value")) {
@@ -47,10 +54,9 @@ export class HaTextArea extends TextAreaBase {
margin-top: 16px;
margin-bottom: 16px;
}
.mdc-floating-label {
inset-inline-start: 16px !important;
inset-inline-end: initial !important;
transform-origin: var(--float-start) top;
:host([dir="rtl"]) .mdc-floating-label {
right: 16px;
left: initial;
}
`,
];

View File

@@ -90,7 +90,7 @@ export class HaTextField extends TextFieldBase {
padding-right: var(--text-field-suffix-padding-right, 0px);
padding-inline-start: var(--text-field-suffix-padding-left, 12px);
padding-inline-end: var(--text-field-suffix-padding-right, 0px);
direction: ltr;
direction: var(--direction);
}
.mdc-text-field--with-leading-icon {
padding-inline-start: var(--text-field-suffix-padding-left, 0px);
@@ -199,6 +199,7 @@ export class HaTextField extends TextFieldBase {
// safari workaround - must be explicit
mainWindow.document.dir === "rtl"
? css`
.mdc-text-field__affix--suffix,
.mdc-text-field--with-leading-icon,
.mdc-text-field__icon--leading,
.mdc-floating-label,

View File

@@ -1,8 +1,35 @@
import "@polymer/paper-toast/paper-toast";
import type { PaperToastElement } from "@polymer/paper-toast/paper-toast";
import { customElement } from "lit/decorators";
import { Snackbar } from "@material/mwc-snackbar/mwc-snackbar";
import type { Constructor } from "../types";
const PaperToast = customElements.get(
"paper-toast"
) as Constructor<PaperToastElement>;
@customElement("ha-toast")
export class HaToast extends Snackbar {}
export class HaToast extends PaperToast {
private _resizeListener?: (obj: { matches: boolean }) => unknown;
private _mediaq?: MediaQueryList;
public connectedCallback() {
super.connectedCallback();
if (!this._resizeListener) {
this._resizeListener = (ev) =>
this.classList.toggle("fit-bottom", ev.matches);
this._mediaq = window.matchMedia("(max-width: 599px");
}
this._mediaq!.addListener(this._resizeListener);
this._resizeListener(this._mediaq!);
}
public disconnectedCallback() {
super.disconnectedCallback();
this._mediaq!.removeListener(this._resizeListener!);
}
}
declare global {
interface HTMLElementTagNameMap {

View File

@@ -133,7 +133,7 @@ export class HaLocationsEditor extends LitElement {
.layers=${this._getLayers(this._circles, this._locationMarkers)}
.zoom=${this.zoom}
.autoFit=${this.autoFit}
?darkMode=${this.darkMode}
.darkMode=${this.darkMode}
></ha-map>
${this.helper
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`

View File

@@ -8,18 +8,12 @@ import type {
Marker,
Polyline,
} from "leaflet";
import { isToday } from "date-fns";
import { css, CSSResultGroup, PropertyValues, ReactiveElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import {
LeafletModuleType,
setupLeafletMap,
} from "../../common/dom/setup-leaflet-map";
import {
formatTimeWithSeconds,
formatTimeWeekday,
} from "../../common/datetime/format_time";
import { formatDateTime } from "../../common/datetime/format_date_time";
import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { computeStateName } from "../../common/entity/compute_state_name";
import { loadPolyfillIfNeeded } from "../../resources/resize-observer.polyfill";
@@ -33,14 +27,12 @@ const getEntityId = (entity: string | HaMapEntity): string =>
export interface HaMapPathPoint {
point: LatLngTuple;
timestamp: Date;
tooltip: string;
}
export interface HaMapPaths {
points: HaMapPathPoint[];
color?: string;
name?: string;
gradualOpacity?: number;
fullDatetime?: boolean;
}
export interface HaMapEntity {
@@ -164,9 +156,9 @@ export class HaMap extends ReactiveElement {
}
private _updateMapStyle(): void {
const darkMode = this.darkMode || (this.hass.themes.darkMode ?? false);
const forcedDark = this.darkMode;
const map = this.renderRoot.querySelector("#map");
const darkMode = this.darkMode ?? this.hass.themes.darkMode ?? false;
const forcedDark = this.darkMode ?? false;
const map = this.shadowRoot!.getElementById("map");
map!.classList.toggle("dark", darkMode);
map!.classList.toggle("forced-dark", forcedDark);
}
@@ -250,30 +242,6 @@ export class HaMap extends ReactiveElement {
});
}
private _computePathTooltip(path: HaMapPaths, point: HaMapPathPoint): string {
let formattedTime: string;
if (path.fullDatetime) {
formattedTime = formatDateTime(
point.timestamp,
this.hass.locale,
this.hass.config
);
} else if (isToday(point.timestamp)) {
formattedTime = formatTimeWithSeconds(
point.timestamp,
this.hass.locale,
this.hass.config
);
} else {
formattedTime = formatTimeWeekday(
point.timestamp,
this.hass.locale,
this.hass.config
);
}
return `${path.name}<br>${formattedTime}`;
}
private _drawPaths(): void {
const hass = this.hass;
const map = this.leafletMap;
@@ -321,10 +289,7 @@ export class HaMap extends ReactiveElement {
fillOpacity: opacity,
interactive: true,
})
.bindTooltip(
this._computePathTooltip(path, path.points[pointIndex]),
{ direction: "top" }
)
.bindTooltip(path.points[pointIndex].tooltip, { direction: "top" })
);
// DRAW line between this and next point
@@ -354,10 +319,7 @@ export class HaMap extends ReactiveElement {
fillOpacity: opacity,
interactive: true,
})
.bindTooltip(
this._computePathTooltip(path, path.points[pointIndex]),
{ direction: "top" }
)
.bindTooltip(path.points[pointIndex].tooltip, { direction: "top" })
);
}
this._mapPaths.forEach((marker) => map.addLayer(marker));
@@ -399,7 +361,7 @@ export class HaMap extends ReactiveElement {
);
const className =
this.darkMode || this.hass.themes.darkMode ? "dark" : "light";
this.darkMode ?? this.hass.themes.darkMode ? "dark" : "light";
for (const entity of this.entities) {
const stateObj = hass.states[getEntityId(entity)];
@@ -594,7 +556,6 @@ export class HaMap extends ReactiveElement {
color: white !important;
border-radius: 4px;
box-shadow: none !important;
text-align: center;
}
`;
}

View File

@@ -223,6 +223,7 @@ class DialogMediaPlayerBrowse extends LitElement {
ha-media-player-browse {
--media-browser-max-height: calc(100vh - 65px);
direction: ltr;
}
:host(.opened) ha-media-player-browse {

View File

@@ -25,6 +25,7 @@ import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import { until } from "lit/directives/until";
import { fireEvent } from "../../common/dom/fire_event";
import { computeRTLDirection } from "../../common/util/compute_rtl";
import { debounce } from "../../common/util/debounce";
import { isUnavailableState } from "../../data/entity";
import type { MediaPlayerItem } from "../../data/media-player";
@@ -538,6 +539,7 @@ export class HaMediaPlayerBrowse extends LitElement {
.graphic=${mediaClass.show_list_images
? "medium"
: "avatar"}
dir=${computeRTLDirection(this.hass)}
>
<span class="title">
${this.hass.localize(
@@ -635,6 +637,7 @@ export class HaMediaPlayerBrowse extends LitElement {
@click=${this._childClicked}
.item=${child}
.graphic=${mediaClass.show_list_images ? "medium" : "avatar"}
dir=${computeRTLDirection(this.hass)}
>
${backgroundImage === "none" && !child.can_play
? html`<ha-svg-icon
@@ -879,7 +882,6 @@ export class HaMediaPlayerBrowse extends LitElement {
display: flex;
flex-direction: column;
position: relative;
direction: ltr;
}
ha-circular-progress {
@@ -1196,8 +1198,10 @@ export class HaMediaPlayerBrowse extends LitElement {
mwc-list-item .title {
margin-left: 16px;
margin-inline-start: 16px;
margin-inline-end: initial;
}
mwc-list-item[dir="rtl"] .title {
margin-right: 16px;
margin-left: 0;
}
/* ============= Narrow ============= */
@@ -1328,10 +1332,6 @@ export class HaMediaPlayerBrowse extends LitElement {
lit-virtualizer.not_shown {
height: calc(100% - 36px);
}
ha-browse-media-tts {
direction: var(--direction);
}
`,
];
}

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