20240202.0Merge branch 'rc'

This commit is contained in:
Bram Kragten 2024-02-02 16:29:21 +01:00
commit d044f4d34e
88 changed files with 805 additions and 1011 deletions

View File

@ -109,7 +109,7 @@
"element-internals-polyfill": "1.3.10",
"fuse.js": "7.0.0",
"google-timezones-json": "1.2.0",
"hls.js": "1.5.2",
"hls.js": "1.5.3",
"home-assistant-js-websocket": "9.1.0",
"idb-keyval": "6.2.1",
"intl-messageformat": "10.5.11",
@ -183,8 +183,8 @@
"@types/tar": "6.1.11",
"@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29",
"@typescript-eslint/eslint-plugin": "6.19.1",
"@typescript-eslint/parser": "6.19.1",
"@typescript-eslint/eslint-plugin": "6.20.0",
"@typescript-eslint/parser": "6.20.0",
"@web/dev-server": "0.1.38",
"@web/dev-server-rollup": "0.4.1",
"babel-loader": "9.1.3",
@ -199,7 +199,7 @@
"eslint-plugin-disable": "2.0.3",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-lit": "1.11.0",
"eslint-plugin-lit-a11y": "4.1.1",
"eslint-plugin-lit-a11y": "4.1.2",
"eslint-plugin-unused-imports": "3.0.0",
"eslint-plugin-wc": "2.0.4",
"fancy-log": "2.0.0",
@ -212,7 +212,7 @@
"gulp-rename": "2.0.0",
"gulp-zopfli-green": "6.0.1",
"html-minifier-terser": "7.2.0",
"husky": "9.0.6",
"husky": "9.0.7",
"instant-mocha": "1.5.2",
"jszip": "3.10.1",
"lint-staged": "15.2.0",

View File

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

View File

@ -38,4 +38,8 @@ 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

@ -10,7 +10,6 @@ import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import { clamp } from "../../common/number/clamp";
import { computeRTL } from "../../common/util/compute_rtl";
import { HomeAssistant } from "../../types";
import { debounce } from "../../common/util/debounce";
@ -212,12 +211,10 @@ export class HaChartBase extends LitElement {
height: `${
this.height ?? this._chartHeight ?? this.clientWidth / 2
}px`,
"padding-left": `${
computeRTL(this.hass) ? 0 : this._paddingYAxisInternal
}px`,
"padding-right": `${
computeRTL(this.hass) ? this._paddingYAxisInternal : 0
}px`,
"padding-left": `${this._paddingYAxisInternal}`,
"padding-right": 0,
"padding-inline-start": `${this._paddingYAxisInternal}`,
"padding-inline-end": 0,
})}
>
<canvas></canvas>
@ -433,14 +430,6 @@ 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%;
@ -449,12 +438,13 @@ export class HaChartBase extends LitElement {
color: white;
border-radius: 4px;
pointer-events: none;
z-index: 1000;
z-index: 1;
-ms-user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
width: 200px;
box-sizing: border-box;
}
:host([rtl]) .chartTooltip {
direction: rtl;
direction: var(--direction);
}
.chartLegend ul,
.chartTooltip ul {

View File

@ -220,7 +220,12 @@ export class StateHistoryChartLine extends LitElement {
// @ts-expect-error
locale: numberFormatToLocale(this.hass.locale),
onClick: (e: any) => {
if (!this.clickForMoreInfo) {
if (
!this.clickForMoreInfo ||
!(e.native instanceof MouseEvent) ||
(e.native instanceof PointerEvent &&
e.native.pointerType !== "mouse")
) {
return;
}

View File

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

View File

@ -688,15 +688,12 @@ 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%;
@ -723,11 +720,7 @@ export class HaDataTable extends LitElement {
}
.mdc-data-table__cell--numeric {
text-align: right;
}
:host([dir="rtl"]) .mdc-data-table__cell--numeric {
/* @noflip */
text-align: left;
text-align: var(--float-end);
}
.mdc-data-table__cell--icon {
@ -753,15 +746,7 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:not(
.not-sorted
) {
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;
text-align: var(--float-start);
}
.mdc-data-table__cell--icon:first-child img,
@ -771,27 +756,14 @@ 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;
}
: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;
margin-inline-start: 8px;
margin-inline-end: initial;
}
.mdc-data-table__cell--icon:first-child state-badge {
margin-right: -8px;
}
:host([dir="rtl"]) .mdc-data-table__cell--icon:first-child state-badge {
margin-right: auto;
margin-left: -8px;
margin-inline-end: -8px;
margin-inline-start: initial;
}
.mdc-data-table__cell--overflow-menu,
@ -824,15 +796,8 @@ 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;
}
: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;
padding-inline-start: 16px;
padding-inline-end: initial; // 8px?
}
.mdc-data-table__cell--overflow-menu:last-child,
@ -840,14 +805,8 @@ 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;
}
: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;
padding-inline-end: 16px;
padding-inline-start: initial; // 8px?
}
.mdc-data-table__cell--overflow-menu,
.mdc-data-table__header-cell--overflow-menu {
@ -867,28 +826,15 @@ export class HaDataTable extends LitElement {
letter-spacing: 0.0071428571em;
text-decoration: inherit;
text-transform: inherit;
text-align: left;
}
:host([dir="rtl"]) .mdc-data-table__header-cell {
/* @noflip */
text-align: right;
text-align: var(--float-start);
}
.mdc-data-table__header-cell--numeric {
text-align: right;
text-align: var(--float-end);
}
.mdc-data-table__header-cell--numeric.sortable:hover,
.mdc-data-table__header-cell--numeric.sortable:not(.not-sorted) {
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;
text-align: var(--float-start);
}
/* custom from here */
@ -909,20 +855,15 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell span {
position: relative;
left: 0px;
}
:host([dir="rtl"]) .mdc-data-table__header-cell span {
left: auto;
right: 0px;
inset-inline-start: 0px;
inset-inline-end: initial;
}
.mdc-data-table__header-cell.sortable {
cursor: pointer;
}
.mdc-data-table__header-cell > * {
transition: left 0.2s ease;
}
:host([dir="rtl"]) .mdc-data-table__header-cell > * {
transition: right 0.2s ease;
transition: var(--float-start) 0.2s ease;
}
.mdc-data-table__header-cell ha-svg-icon {
top: -3px;
@ -930,35 +871,20 @@ export class HaDataTable extends LitElement {
}
.mdc-data-table__header-cell.not-sorted ha-svg-icon {
left: -20px;
}
:host([dir="rtl"]) .mdc-data-table__header-cell.not-sorted ha-svg-icon {
right: -20px;
inset-inline-start: -20px;
inset-inline-end: initial;
}
.mdc-data-table__header-cell.sortable:not(.not-sorted) span,
.mdc-data-table__header-cell.sortable.not-sorted:hover span {
left: 24px;
}
: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;
inset-inline-start: 24px;
inset-inline-end: initial;
}
.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;
}
: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;
inset-inline-start: 12px;
inset-inline-end: initial;
}
.table-header {
border-bottom: 1px solid var(--divider-color);
@ -966,6 +892,8 @@ 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,6 +9,7 @@ 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({
@ -157,7 +158,7 @@ class DateRangePickerElement extends WrappedElement {
min-width: initial !important;
max-height: var(--date-range-picker-max-height);
overflow-y: auto;
}
}
.daterangepicker:before {
display: none;
}
@ -267,15 +268,37 @@ class DateRangePickerElement extends WrappedElement {
.calendar-table {
padding: 0 !important;
}
.daterangepicker.ltr {
.calendar-time {
direction: ltr;
text-align: left;
}
.daterangepicker.ltr {
direction: var(--direction);
text-align: var(--float-start);
}
.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

@ -3,7 +3,6 @@ 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";
@ -16,9 +15,6 @@ 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() {
@ -79,18 +75,6 @@ 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 {
@ -106,17 +90,14 @@ 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;
}
:host([rtl]) .info {
margin-right: 8px;
margin-left: 0;
text-align: right;
text-align: var(--float-start);
}
.name {

View File

@ -69,6 +69,7 @@ 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;
@ -119,19 +120,6 @@ 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

@ -32,7 +32,6 @@ 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";
@ -65,8 +64,6 @@ 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";
@ -236,7 +233,6 @@ 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);
}
}
}
@ -306,11 +302,7 @@ export class HaDateRangePicker extends LitElement {
></ha-icon-button>`}
</div>
${this.ranges !== false && (this.ranges || this._ranges)
? html`<div
slot="ranges"
class="date-range-ranges"
.dir=${this._rtlDirection}
>
? html`<div slot="ranges" class="date-range-ranges">
<mwc-list @action=${this._setDateRange} activatable>
${Object.keys(this.ranges || this._ranges!).map(
(name) => html`<mwc-list-item>${name}</mwc-list-item>`

View File

@ -3,6 +3,7 @@ import { styles } from "@material/mwc-drawer/mwc-drawer.css";
import { css, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import { mainWindow } from "../common/dom/get_main_window";
const blockingElements = (document as any).$blockingElements;
@ -12,6 +13,8 @@ export class HaDrawer extends DrawerBase {
private _mc?: HammerManager;
private _rtlStyle?: HTMLElement;
protected createAdapter() {
return {
...super.createAdapter(),
@ -31,8 +34,26 @@ export class HaDrawer extends DrawerBase {
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (changedProps.has("direction")) {
this.mdcRoot.dir = this.direction;
if (mainWindow.document.dir === "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) {
@ -66,6 +87,8 @@ 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

@ -38,7 +38,6 @@ 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 {
@ -307,16 +306,12 @@ 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) {
@ -851,29 +846,22 @@ class HaSidebar extends SubscribeMixin(LitElement) {
font-size: 20px;
align-items: center;
padding-left: calc(4px + env(safe-area-inset-left));
}
:host([rtl]) .menu {
padding-left: 4px;
padding-right: calc(4px + env(safe-area-inset-right));
padding-inline-start: calc(4px + env(safe-area-inset-left));
padding-inline-end: initial;
}
: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;
@ -904,11 +892,8 @@ class HaSidebar extends SubscribeMixin(LitElement) {
overflow-x: hidden;
background: none;
margin-left: env(safe-area-inset-left);
}
:host([rtl]) paper-listbox {
margin-left: initial;
margin-right: env(safe-area-inset-right);
margin-inline-start: env(safe-area-inset-left);
margin-inline-end: initial;
}
a {
@ -925,6 +910,8 @@ 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;
@ -932,10 +919,6 @@ 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"] {
@ -1010,11 +993,8 @@ class HaSidebar extends SubscribeMixin(LitElement) {
.configuration-container {
display: flex;
margin-left: env(safe-area-inset-left);
}
:host([rtl]) .notifications-container,
:host([rtl]) .configuration-container {
margin-left: initial;
margin-right: env(safe-area-inset-right);
margin-inline-start: env(safe-area-inset-left);
margin-inline-end: initial;
}
.notifications {
cursor: pointer;
@ -1025,23 +1005,18 @@ class HaSidebar extends SubscribeMixin(LitElement) {
}
.profile {
margin-left: env(safe-area-inset-left);
}
:host([rtl]) .profile {
margin-left: initial;
margin-right: env(safe-area-inset-right);
margin-inline-start: env(safe-area-inset-left);
margin-inline-end: initial;
}
.profile paper-icon-item {
padding-left: 4px;
}
:host([rtl]) .profile paper-icon-item {
padding-left: auto;
padding-right: 4px;
margin-inline-start: 4px;
margin-inline-end: auto;
}
.profile .item-text {
margin-left: 8px;
}
:host([rtl]) .profile .item-text {
margin-right: 8px;
margin-inline-start: 8px;
margin-inline-end: initial;
}
.notification-badge,
@ -1106,9 +1081,9 @@ class HaSidebar extends SubscribeMixin(LitElement) {
font-weight: 500;
}
:host([rtl]) .menu ha-icon-button {
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
.menu ha-icon-button {
-webkit-transform: scaleX(var(--scale-direction));
transform: scaleX(var(--scale-direction));
}
`,
];

View File

@ -2,9 +2,15 @@ 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

@ -39,6 +39,12 @@ export class HaSortable extends LitElement {
@property({ type: String, attribute: "group" })
public group?: string;
@property({ type: Number, attribute: "swap-threshold" })
public swapThreshold?: number;
@property({ type: Boolean, attribute: "invert-swap" })
public invertSwap?: boolean;
protected updated(changedProperties: PropertyValues<this>) {
if (changedProperties.has("disabled")) {
if (this.disabled) {
@ -81,7 +87,7 @@ export class HaSortable extends LitElement {
}
.sortable-ghost {
border: 2px solid var(--primary-color);
box-shadow: 0 0 0 2px var(--primary-color);
background: rgba(var(--rgb-primary-color), 0.25);
border-radius: 4px;
opacity: 0.4;
@ -108,7 +114,7 @@ export class HaSortable extends LitElement {
const options: SortableInstance.Options = {
animation: 150,
swapThreshold: 0.75,
swapThreshold: 1,
onChoose: this._handleChoose,
onEnd: this._handleEnd,
};
@ -116,6 +122,13 @@ export class HaSortable extends LitElement {
if (this.draggableSelector) {
options.draggable = this.draggableSelector;
}
if (this.swapThreshold !== undefined) {
options.swapThreshold = this.swapThreshold;
}
if (this.invertSwap !== undefined) {
options.invertSwap = this.invertSwap;
}
if (this.handleSelector) {
options.handle = this.handleSelector;
}

View File

@ -3,18 +3,11 @@ 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")) {
@ -54,9 +47,10 @@ export class HaTextArea extends TextAreaBase {
margin-top: 16px;
margin-bottom: 16px;
}
:host([dir="rtl"]) .mdc-floating-label {
right: 16px;
left: initial;
.mdc-floating-label {
inset-inline-start: 16px !important;
inset-inline-end: initial !important;
transform-origin: var(--float-start) top;
}
`,
];

View File

@ -8,12 +8,18 @@ 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";
@ -27,12 +33,14 @@ const getEntityId = (entity: string | HaMapEntity): string =>
export interface HaMapPathPoint {
point: LatLngTuple;
tooltip: string;
timestamp: Date;
}
export interface HaMapPaths {
points: HaMapPathPoint[];
color?: string;
name?: string;
gradualOpacity?: number;
fullDatetime?: boolean;
}
export interface HaMapEntity {
@ -242,6 +250,30 @@ 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;
@ -289,7 +321,10 @@ export class HaMap extends ReactiveElement {
fillOpacity: opacity,
interactive: true,
})
.bindTooltip(path.points[pointIndex].tooltip, { direction: "top" })
.bindTooltip(
this._computePathTooltip(path, path.points[pointIndex]),
{ direction: "top" }
)
);
// DRAW line between this and next point
@ -319,7 +354,10 @@ export class HaMap extends ReactiveElement {
fillOpacity: opacity,
interactive: true,
})
.bindTooltip(path.points[pointIndex].tooltip, { direction: "top" })
.bindTooltip(
this._computePathTooltip(path, path.points[pointIndex]),
{ direction: "top" }
)
);
}
this._mapPaths.forEach((marker) => map.addLayer(marker));
@ -556,6 +594,7 @@ export class HaMap extends ReactiveElement {
color: white !important;
border-radius: 4px;
box-shadow: none !important;
text-align: center;
}
`;
}

View File

@ -25,7 +25,6 @@ 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";
@ -539,7 +538,6 @@ export class HaMediaPlayerBrowse extends LitElement {
.graphic=${mediaClass.show_list_images
? "medium"
: "avatar"}
dir=${computeRTLDirection(this.hass)}
>
<span class="title">
${this.hass.localize(
@ -637,7 +635,6 @@ 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
@ -1198,10 +1195,8 @@ export class HaMediaPlayerBrowse extends LitElement {
mwc-list-item .title {
margin-left: 16px;
}
mwc-list-item[dir="rtl"] .title {
margin-right: 16px;
margin-left: 0;
margin-inline-start: 16px;
margin-inline-end: initial;
}
/* ============= Narrow ============= */
@ -1332,6 +1327,10 @@ export class HaMediaPlayerBrowse extends LitElement {
lit-virtualizer.not_shown {
height: calc(100% - 36px);
}
ha-browse-media-tts {
direction: var(--direction);
}
`,
];
}

View File

@ -8,30 +8,41 @@ import {
EntityRegistryDisplayEntry,
EntityRegistryEntry,
} from "./entity_registry";
import { isComponentLoaded } from "../common/config/is_component_loaded";
const resources: Record<IconCategory, any> = {
const resources: {
entity: Record<string, Promise<PlatformIcons>>;
entity_component: {
domains?: string[];
resources?: Promise<Record<string, ComponentIcons>>;
};
services: {
all?: Promise<Record<string, ServiceIcons>>;
domains: { [domain: string]: ServiceIcons | Promise<ServiceIcons> };
};
} = {
entity: {},
entity_component: undefined,
services: {},
entity_component: {},
services: { domains: {} },
};
interface IconResources {
resources: Record<string, string | Record<string, string>>;
interface IconResources<
T extends ComponentIcons | PlatformIcons | ServiceIcons,
> {
resources: Record<string, T>;
}
interface PlatformIcons {
[domain: string]: {
[translation_key: string]: {
state: Record<string, string>;
state_attributes: Record<
string,
{
state: Record<string, string>;
default: string;
}
>;
default: string;
};
[translation_key: string]: {
state: Record<string, string>;
state_attributes: Record<
string,
{
state: Record<string, string>;
default: string;
}
>;
default: string;
};
}
@ -55,12 +66,18 @@ interface ServiceIcons {
export type IconCategory = "entity" | "entity_component" | "services";
export const getHassIcons = async (
type CategoryType = {
entity: PlatformIcons;
entity_component: ComponentIcons;
services: ServiceIcons;
};
export const getHassIcons = async <T extends IconCategory>(
hass: HomeAssistant,
category: IconCategory,
category: T,
integration?: string
): Promise<IconResources> =>
hass.callWS<{ resources: Record<string, string> }>({
) =>
hass.callWS<IconResources<CategoryType[T]>>({
type: "frontend/get_icons",
category,
integration,
@ -70,14 +87,17 @@ export const getPlatformIcons = async (
hass: HomeAssistant,
integration: string,
force = false
): Promise<PlatformIcons> => {
): Promise<PlatformIcons | undefined> => {
if (!force && integration in resources.entity) {
return resources.entity[integration];
}
const result = getHassIcons(hass, "entity", integration);
resources.entity[integration] = result.then(
if (!isComponentLoaded(hass, integration)) {
return undefined;
}
const result = getHassIcons(hass, "entity", integration).then(
(res) => res?.resources[integration]
);
resources.entity[integration] = result;
return resources.entity[integration];
};
@ -85,45 +105,59 @@ export const getComponentIcons = async (
hass: HomeAssistant,
domain: string,
force = false
): Promise<ComponentIcons> => {
if (!force && resources.entity_component) {
return resources.entity_component.then((res) => res[domain]);
): Promise<ComponentIcons | undefined> => {
if (
!force &&
resources.entity_component.resources &&
resources.entity_component.domains?.includes(domain)
) {
return resources.entity_component.resources.then((res) => res[domain]);
}
resources.entity_component = getHassIcons(hass, "entity_component").then(
(result) => result.resources
);
return resources.entity_component.then((res) => res[domain]);
if (!isComponentLoaded(hass, domain)) {
return undefined;
}
resources.entity_component.domains = [...hass.config.components];
resources.entity_component.resources = getHassIcons(
hass,
"entity_component"
).then((result) => result.resources);
return resources.entity_component.resources.then((res) => res[domain]);
};
export const getServiceIcons = async (
hass: HomeAssistant,
domain?: string,
force = false
): Promise<ServiceIcons> => {
): Promise<ServiceIcons | Record<string, ServiceIcons> | undefined> => {
if (!domain) {
if (!force && resources.services.all) {
return resources.services.all;
}
resources.services.all = getHassIcons(hass, "services", domain).then(
(res) => {
resources.services = res.resources;
resources.services.domains = res.resources;
return res?.resources;
}
);
return resources.services.all;
}
if (!force && domain && domain in resources.services) {
return resources.services[domain];
if (!force && domain in resources.services.domains) {
return resources.services.domains[domain];
}
if (resources.services.all && !force) {
await resources.services.all;
if (domain in resources.services) {
return resources.services[domain];
if (domain in resources.services.domains) {
return resources.services.domains[domain];
}
}
if (!isComponentLoaded(hass, domain)) {
return undefined;
}
const result = getHassIcons(hass, "services", domain);
resources.services[domain] = result.then((res) => res?.resources[domain]);
return resources.services[domain];
resources.services.domains[domain] = result.then(
(res) => res?.resources[domain]
);
return resources.services.domains[domain];
};
export const entityIcon = async (
@ -238,7 +272,7 @@ export const serviceIcon = async (
const serviceName = computeObjectId(service);
const serviceIcons = await getServiceIcons(hass, domain);
if (serviceIcons) {
icon = serviceIcons[serviceName];
icon = serviceIcons[serviceName] as string;
}
if (!icon) {
icon = await domainIcon(hass, domain);

View File

@ -2,7 +2,6 @@ import "@material/mwc-button/mwc-button";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { computeRTLDirection } from "../../common/util/compute_rtl";
import "../../components/ha-dialog";
import "../../components/ha-formfield";
import "../../components/ha-switch";
@ -82,7 +81,6 @@ class DialogConfigEntrySystemOptions extends LitElement {
}
)}
</p>`}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.checked=${!this._disableNewEntities}
@ -109,7 +107,6 @@ class DialogConfigEntrySystemOptions extends LitElement {
}
)}
</p>`}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.checked=${!this._disablePolling}

View File

@ -14,7 +14,6 @@ import { customElement, property } from "lit/decorators";
import { stopPropagation } from "../../../common/dom/stop_propagation";
import { stateActive } from "../../../common/entity/state_active";
import { supportsFeature } from "../../../common/entity/supports-feature";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import "../../../components/ha-icon-button";
import "../../../components/ha-select";
import "../../../components/ha-slider";
@ -131,7 +130,6 @@ class MoreInfoMediaPlayer extends LitElement {
<ha-slider
labeled
id="input"
.dir=${computeRTLDirection(this.hass!)}
.value=${Number(stateObj.attributes.volume_level) * 100}
@change=${this._selectedValueChanged}
></ha-slider>

View File

@ -1,15 +1,6 @@
import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
TemplateResult,
} from "lit";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, eventOptions, property } from "lit/decorators";
import { restoreScroll } from "../common/decorators/restore-scroll";
import { toggleAttribute } from "../common/dom/toggle_attribute";
import { computeRTL } from "../common/util/compute_rtl";
import "../components/ha-icon-button-arrow-prev";
import "../components/ha-menu-button";
import { HomeAssistant } from "../types";
@ -34,17 +25,6 @@ class HassSubpage extends LitElement {
// @ts-ignore
@restoreScroll(".content") private _savedScrollPos?: number;
protected willUpdate(changedProps: PropertyValues): void {
super.willUpdate(changedProps);
if (!changedProps.has("hass")) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass.locale) {
toggleAttribute(this, "rtl", computeRTL(this.hass));
}
}
protected render(): TemplateResult {
return html`
<div class="toolbar">
@ -160,6 +140,9 @@ class HassSubpage extends LitElement {
#fab {
position: absolute;
right: calc(16px + env(safe-area-inset-right));
inset-inline-end: calc(16px + env(safe-area-inset-right));
inset-inline-start: initial;
bottom: calc(16px + env(safe-area-inset-bottom));
z-index: 1;
}
@ -169,15 +152,8 @@ class HassSubpage extends LitElement {
#fab[is-wide] {
bottom: 24px;
right: 24px;
}
:host([rtl]) #fab {
right: auto;
left: calc(16px + env(safe-area-inset-left));
}
:host([rtl][is-wide]) #fab {
bottom: 24px;
left: 24px;
right: auto;
inset-inline-end: 24px;
inset-inline-start: initial;
}
`,
];

View File

@ -4,7 +4,6 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import { LocalizeFunc } from "../common/translations/localize";
import { computeRTLDirection } from "../common/util/compute_rtl";
import "../components/data-table/ha-data-table";
import type {
DataTableColumnContainer,
@ -244,7 +243,6 @@ export class HaTabsSubpageDataTable extends LitElement {
.selectable=${this.selectable}
.hasFab=${this.hasFab}
.id=${this.id}
.dir=${computeRTLDirection(this.hass)}
.clickable=${this.clickable}
.appendRow=${this.appendRow}
>

View File

@ -13,7 +13,6 @@ import memoizeOne from "memoize-one";
import { isComponentLoaded } from "../common/config/is_component_loaded";
import { restoreScroll } from "../common/decorators/restore-scroll";
import { LocalizeFunc } from "../common/translations/localize";
import { computeRTL } from "../common/util/compute_rtl";
import "../components/ha-icon-button-arrow-prev";
import "../components/ha-menu-button";
import "../components/ha-svg-icon";
@ -58,8 +57,6 @@ class HassTabsSubpage extends LitElement {
@property({ type: Boolean, reflect: true, attribute: "is-wide" })
public isWide = false;
@property({ type: Boolean, reflect: true }) public rtl = false;
@state() private _activeTab?: PageNavigation;
// @ts-ignore
@ -123,14 +120,6 @@ class HassTabsSubpage extends LitElement {
`${this.route.prefix}${this.route.path}`.includes(tab.path)
);
}
if (changedProperties.has("hass")) {
const oldHass = changedProperties.get("hass") as
| HomeAssistant
| undefined;
if (!oldHass || oldHass.language !== this.hass.language) {
this.rtl = computeRTL(this.hass);
}
}
super.willUpdate(changedProperties);
}
@ -334,6 +323,8 @@ class HassTabsSubpage extends LitElement {
#fab {
position: fixed;
right: calc(16px + env(safe-area-inset-right));
inset-inline-end: calc(16px + env(safe-area-inset-right));
inset-inline-start: initial;
bottom: calc(16px + env(safe-area-inset-bottom));
z-index: 1;
}
@ -343,15 +334,8 @@ class HassTabsSubpage extends LitElement {
#fab[is-wide] {
bottom: 24px;
right: 24px;
}
:host([rtl]) #fab {
right: auto;
left: calc(16px + env(safe-area-inset-left));
}
:host([rtl][is-wide]) #fab {
bottom: 24px;
left: 24px;
right: auto;
inset-inline-end: 24px;
inset-inline-start: initial;
}
`,
];

View File

@ -11,7 +11,6 @@ import { customElement, property, state } from "lit/decorators";
import { fireEvent, HASSDomEvent } from "../common/dom/fire_event";
import { listenMediaQuery } from "../common/dom/media_query";
import { toggleAttribute } from "../common/dom/toggle_attribute";
import { computeRTLDirection } from "../common/util/compute_rtl";
import "../components/ha-drawer";
import { showNotificationDrawer } from "../dialogs/notifications/show-notification-drawer";
import type { HomeAssistant, Route } from "../types";
@ -62,7 +61,6 @@ export class HomeAssistantMain extends LitElement {
<ha-drawer
.type=${sidebarNarrow ? "modal" : ""}
.open=${sidebarNarrow ? this._drawerOpen : undefined}
.direction=${computeRTLDirection(this.hass)}
@MDCDrawer:closed=${this._drawerClosed}
>
<ha-sidebar

View File

@ -28,7 +28,6 @@ import { useAmPm } from "../../common/datetime/use_am_pm";
import { fireEvent } from "../../common/dom/fire_event";
import { supportsFeature } from "../../common/entity/supports-feature";
import { LocalizeFunc } from "../../common/translations/localize";
import { computeRTLDirection } from "../../common/util/compute_rtl";
import "../../components/ha-button-toggle-group";
import "../../components/ha-fab";
import "../../components/ha-icon-button-next";
@ -169,7 +168,6 @@ export class HAFullCalendar extends LitElement {
.buttons=${viewToggleButtons}
.active=${this._activeView}
@value-changed=${this._handleView}
.dir=${computeRTLDirection(this.hass)}
></ha-button-toggle-group>
`
: html`
@ -203,7 +201,6 @@ export class HAFullCalendar extends LitElement {
.buttons=${viewToggleButtons}
.active=${this._activeView}
@value-changed=${this._handleView}
.dir=${computeRTLDirection(this.hass)}
></ha-button-toggle-group>
</div>
`}
@ -503,6 +500,8 @@ export class HAFullCalendar extends LitElement {
position: absolute;
bottom: 32px;
right: 32px;
inset-inline-end: 32px;
inset-inline-start: initial;
z-index: 1;
}

View File

@ -81,6 +81,7 @@ export default class HaAutomationAction extends LitElement {
@item-moved=${this._actionMoved}
group="actions"
.path=${this.path}
invert-swap
>
<div class="actions">
${repeat(
@ -266,22 +267,32 @@ export default class HaAutomationAction extends LitElement {
static get styles(): CSSResultGroup {
return css`
.actions {
padding: 16px;
margin: -16px -16px 0px -16px;
display: flex;
flex-direction: column;
gap: 16px;
}
.actions:not(:has(ha-automation-action-row)) {
margin: -16px;
}
.sortable-ghost {
background: none;
border-radius: var(--ha-card-border-radius, 12px);
}
.sortable-drag {
background: none;
}
ha-automation-action-row {
display: block;
margin-bottom: 16px;
scroll-margin-top: 48px;
}
ha-svg-icon {
height: 20px;
}
ha-alert {
display: block;
margin-bottom: 16px;
border-radius: var(--ha-card-border-radius, 12px);
overflow: hidden;
}
.handle {
padding: 12px 4px;
padding: 12px;
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}

View File

@ -125,6 +125,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
.disabled=${!this._showReorder || this.disabled}
group="choose-options"
.path=${[...(this.path ?? []), "choose"]}
invert-swap
>
<div class="options">
${repeat(
@ -296,7 +297,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
)}:
</h2>
<ha-automation-action
.path=${[...(this.path ?? []), "choose", "default"]}
.path=${[...(this.path ?? []), "default"]}
.actions=${ensureArray(action.default) || []}
.disabled=${this.disabled}
@value-changed=${this._defaultChanged}
@ -502,8 +503,22 @@ export class HaChooseAction extends LitElement implements ActionElement {
return [
haStyle,
css`
.option {
margin: 0 0 16px 0;
.options {
padding: 16px;
margin: -16px -16px 0px -16px;
display: flex;
flex-direction: column;
gap: 16px;
}
.options:not(:has(.option)) {
margin: -16px;
}
.sortable-ghost {
background: none;
border-radius: var(--ha-card-border-radius, 12px);
}
.sortable-drag {
background: none;
}
.add-card mwc-button {
display: block;
@ -539,7 +554,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
padding: 0 16px 16px 16px;
}
.handle {
padding: 12px 4px;
padding: 12px;
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}

View File

@ -121,6 +121,7 @@ export default class HaAutomationCondition extends LitElement {
@item-moved=${this._conditionMoved}
group="conditions"
.path=${this.path}
invert-swap
>
<div class="conditions">
${repeat(
@ -291,22 +292,32 @@ export default class HaAutomationCondition extends LitElement {
static get styles(): CSSResultGroup {
return css`
.conditions {
padding: 16px;
margin: -16px -16px 0px -16px;
display: flex;
flex-direction: column;
gap: 16px;
}
.conditions:not(:has(ha-automation-condition-row)) {
margin: -16px;
}
.sortable-ghost {
background: none;
border-radius: var(--ha-card-border-radius, 12px);
}
.sortable-drag {
background: none;
}
ha-automation-condition-row {
display: block;
margin-bottom: 16px;
scroll-margin-top: 48px;
}
ha-svg-icon {
height: 20px;
}
ha-alert {
display: block;
margin-bottom: 16px;
border-radius: var(--ha-card-border-radius, 12px);
overflow: hidden;
}
.handle {
padding: 12px 4px;
padding: 12px;
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}

View File

@ -1,5 +1,4 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import {
mdiCheck,
mdiContentDuplicate,
@ -35,6 +34,7 @@ import "../../../components/ha-fab";
import "../../../components/ha-icon-button";
import "../../../components/ha-svg-icon";
import "../../../components/ha-yaml-editor";
import "../../../components/ha-list-item";
import {
AutomationConfig,
AutomationEntity,
@ -150,7 +150,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${!stateObj}
@click=${this._showInfo}
@ -160,20 +160,20 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
slot="graphic"
.path=${mdiInformationOutline}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${!stateObj}
@click=${this._runActions}
>
${this.hass.localize("ui.panel.config.automation.editor.run")}
<ha-svg-icon slot="graphic" .path=${mdiPlay}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
${stateObj && this._config && this.narrow
? html`<a href="/config/automation/trace/${this._config.id}">
<mwc-list-item graphic="icon">
<ha-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.show_trace"
)}
@ -181,22 +181,22 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
slot="graphic"
.path=${mdiTransitConnection}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
</a>`
: ""}
<mwc-list-item
<ha-list-item
graphic="icon"
@click=${this._promptAutomationAlias}
.disabled=${!this.automationId || this._mode === "yaml"}
>
${this.hass.localize("ui.panel.config.automation.editor.rename")}
<ha-svg-icon slot="graphic" .path=${mdiRenameBox}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
${this._config && !("use_blueprint" in this._config)
? html`
<mwc-list-item
<ha-list-item
graphic="icon"
@click=${this._promptAutomationMode}
.disabled=${this._readOnly || this._mode === "yaml"}
@ -208,11 +208,11 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
slot="graphic"
.path=${mdiDebugStepOver}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
`
: ""}
<mwc-list-item
<ha-list-item
.disabled=${!this._readOnly && !this.automationId}
graphic="icon"
@click=${this._duplicate}
@ -226,11 +226,11 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
slot="graphic"
.path=${mdiContentDuplicate}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<li divider role="separator"></li>
<mwc-list-item graphic="icon" @click=${this._switchUiMode}>
<ha-list-item graphic="icon" @click=${this._switchUiMode}>
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
${this._mode === "gui"
? html`<ha-svg-icon
@ -239,8 +239,8 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item graphic="icon" @click=${this._switchYamlMode}>
</ha-list-item>
<ha-list-item graphic="icon" @click=${this._switchYamlMode}>
${this.hass.localize("ui.panel.config.automation.editor.edit_yaml")}
${this._mode === "yaml"
? html`<ha-svg-icon
@ -249,11 +249,11 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
</ha-list-item>
<li divider role="separator"></li>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${!stateObj}
@click=${this._toggle}
@ -267,9 +267,9 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
? mdiPlayCircleOutline
: mdiStopCircleOutline}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item
<ha-list-item
.disabled=${!this.automationId}
class=${classMap({ warning: Boolean(this.automationId) })}
graphic="icon"
@ -282,7 +282,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
.path=${mdiDelete}
>
</ha-svg-icon>
</mwc-list-item>
</ha-list-item>
</ha-button-menu>
${this._config

View File

@ -78,6 +78,7 @@ export default class HaAutomationTrigger extends LitElement {
@item-moved=${this._triggerMoved}
group="triggers"
.path=${this.path}
invert-swap
>
<div class="triggers">
${repeat(
@ -240,22 +241,32 @@ export default class HaAutomationTrigger extends LitElement {
static get styles(): CSSResultGroup {
return css`
.triggers {
padding: 16px;
margin: -16px -16px 0px -16px;
display: flex;
flex-direction: column;
gap: 16px;
}
.triggers:not(:has(ha-automation-trigger-row)) {
margin: -16px;
}
.sortable-ghost {
background: none;
border-radius: var(--ha-card-border-radius, 12px);
}
.sortable-drag {
background: none;
}
ha-automation-trigger-row {
display: block;
margin-bottom: 16px;
scroll-margin-top: 48px;
}
ha-svg-icon {
height: 20px;
}
ha-alert {
display: block;
margin-bottom: 16px;
border-radius: var(--ha-card-border-radius, 16px);
overflow: hidden;
}
.handle {
padding: 12px 4px;
padding: 12px;
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}

View File

@ -1,9 +1,8 @@
import "@material/mwc-button";
import { css, html, LitElement, PropertyValues } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { formatDateTime } from "../../../../common/datetime/format_date_time";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import { debounce } from "../../../../common/util/debounce";
import "../../../../components/ha-alert";
import "../../../../components/ha-card";
@ -37,8 +36,6 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
@state() private _subscription?: SubscriptionInfo;
@state() private _rtlDirection: "rtl" | "ltr" = "rtl";
protected render() {
return html`
<hass-subpage
@ -172,13 +169,11 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
<cloud-remote-pref
.hass=${this.hass}
.cloudStatus=${this.cloudStatus}
dir=${this._rtlDirection}
></cloud-remote-pref>
<cloud-tts-pref
.hass=${this.hass}
.cloudStatus=${this.cloudStatus}
dir=${this._rtlDirection}
></cloud-tts-pref>
<ha-tip .hass=${this.hass}>
@ -193,7 +188,6 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
.hass=${this.hass}
.narrow=${this.narrow}
.cloudStatus=${this.cloudStatus}
dir=${this._rtlDirection}
></cloud-webhooks>
</ha-config-section>
</div>
@ -205,15 +199,6 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
this._fetchSubscriptionInfo();
}
protected updated(changedProps: PropertyValues) {
if (changedProps.has("hass")) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass.locale) {
this._rtlDirection = computeRTLDirection(this.hass);
}
}
}
protected override hassSubscribe() {
const googleCheck = debounce(
() => {
@ -272,10 +257,6 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
fireEvent(this, "ha-refresh-cloud-status");
}
_computeRTLDirection(hass) {
return computeRTLDirection(hass);
}
static get styles() {
return [
haStyle,

View File

@ -179,14 +179,12 @@ export class CloudRemotePref extends LitElement {
.header-actions {
position: absolute;
right: 24px;
inset-inline-end: 24px;
inset-inline-start: initial;
top: 24px;
display: flex;
flex-direction: row;
}
:host([dir="rtl"]) .header-actions {
right: auto;
left: 24px;
}
.header-actions .icon-link {
margin-top: -16px;
margin-right: 8px;

View File

@ -177,12 +177,10 @@ export class CloudTTSPref extends LitElement {
.example {
position: absolute;
right: 16px;
inset-inline-end: 16px;
inset-inline-start: initial;
top: 16px;
}
:host([dir="rtl"]) .example {
right: auto;
left: 24px;
}
.row {
display: flex;
}

View File

@ -9,7 +9,6 @@ import {
} from "lit";
import { customElement, state } from "lit/decorators";
import { computeStateName } from "../../../../../../common/entity/compute_state_name";
import { computeRTLDirection } from "../../../../../../common/util/compute_rtl";
import "../../../../../../components/ha-dialog";
import "../../../../../../components/ha-formfield";
import "../../../../../../components/ha-switch";
@ -51,8 +50,6 @@ class DialogMQTTDeviceDebugInfo extends LitElement {
return nothing;
}
const dir = computeRTLDirection(this.hass!);
return html`
<ha-dialog
open
@ -72,7 +69,6 @@ class DialogMQTTDeviceDebugInfo extends LitElement {
.label=${this.hass!.localize(
"ui.dialogs.mqtt_device_debug_info.deserialize"
)}
.dir=${dir}
>
<ha-switch
.checked=${this._showDeserialized}
@ -87,7 +83,6 @@ class DialogMQTTDeviceDebugInfo extends LitElement {
.label=${this.hass!.localize(
"ui.dialogs.mqtt_device_debug_info.show_as_yaml"
)}
.dir=${dir}
>
<ha-switch
.checked=${this._showAsYaml}

View File

@ -20,7 +20,6 @@ import {
} from "../../../common/integrations/protocolIntegrationPicked";
import { navigate } from "../../../common/navigate";
import { LocalizeFunc } from "../../../common/translations/localize";
import { computeRTL } from "../../../common/util/compute_rtl";
import {
DataTableColumnContainer,
RowClickedEvent,
@ -488,7 +487,6 @@ export class HaConfigDeviceDashboard extends LitElement {
.label=${this.hass.localize("ui.panel.config.devices.add_device")}
extended
@click=${this._addDevice}
?rtl=${computeRTL(this.hass)}
>
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
</ha-fab>

View File

@ -35,7 +35,6 @@ import {
} from "../../../common/integrations/protocolIntegrationPicked";
import { navigate } from "../../../common/navigate";
import { LocalizeFunc } from "../../../common/translations/localize";
import { computeRTL } from "../../../common/util/compute_rtl";
import type {
DataTableColumnContainer,
RowClickedEvent,
@ -680,7 +679,6 @@ export class HaConfigEntities extends LitElement {
extended
@click=${this._addDevice}
slot="fab"
?rtl=${computeRTL(this.hass)}
>
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
</ha-fab>`

View File

@ -1,5 +1,5 @@
import "@material/mwc-button/mwc-button";
import { mdiClose } from "@mdi/js";
import { mdiDelete } from "@mdi/js";
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
@ -70,7 +70,7 @@ class DialogMatterManageFabrics extends LitElement {
@click=${this._removeFabric}
slot="meta"
.fabric=${fabric}
.path=${mdiClose}
.path=${mdiDelete}
></ha-icon-button>
</ha-list-item>`
)}

View File

@ -48,6 +48,11 @@ class DialogMatterOpenCommissioningWindow extends LitElement {
>
${this._commissionParams
? html`
<p>
${this.hass.localize(
"ui.panel.config.matter.open_commissioning_window.success"
)}
</p>
<div class="flex-container">
<ha-svg-icon
.path=${mdiCheckCircle}

View File

@ -1,7 +1,6 @@
import { html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import memoizeOne from "memoize-one";
import { computeRTLDirection } from "../../../../../common/util/compute_rtl";
import "../../../../../components/data-table/ha-data-table";
import type {
DataTableColumnContainer,
@ -82,7 +81,6 @@ export class ZHAClustersDataTable extends LitElement {
.id=${"cluster_id"}
selectable
auto-height
.dir=${computeRTLDirection(this.hass)}
.searchLabel=${this.hass.localize("ui.components.data-table.search")}
.noDataText=${this.hass.localize("ui.components.data-table.no-data")}
></ha-data-table>

View File

@ -19,7 +19,6 @@ import {
ConfigEntry,
getConfigEntries,
} from "../../../../../data/config_entries";
import { computeRTL } from "../../../../../common/util/compute_rtl";
import "../../../../../components/ha-card";
import "../../../../../components/ha-fab";
import "../../../../../components/ha-icon-button";
@ -259,7 +258,6 @@ class ZHAConfigDashboard extends LitElement {
<ha-fab
.label=${this.hass.localize("ui.panel.config.zha.add_device")}
extended
?rtl=${computeRTL(this.hass)}
>
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
</ha-fab>

View File

@ -1,7 +1,6 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import memoizeOne from "memoize-one";
import { computeRTLDirection } from "../../../../../common/util/compute_rtl";
import "../../../../../components/data-table/ha-data-table";
import type {
DataTableColumnContainer,
@ -142,7 +141,6 @@ export class ZHADeviceEndpointDataTable extends LitElement {
.data=${this._deviceEndpoints(this.deviceEndpoints)}
.selectable=${this.selectable}
auto-height
.dir=${computeRTLDirection(this.hass)}
.searchLabel=${this.hass.localize("ui.components.data-table.search")}
.noDataText=${this.hass.localize("ui.components.data-table.no-data")}
></ha-data-table>

View File

@ -1,7 +1,6 @@
import { html, LitElement, PropertyValues, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { computeRTLDirection } from "../../../../../common/util/compute_rtl";
import "../../../../../components/data-table/ha-data-table";
import type {
DataTableColumnContainer,
@ -128,7 +127,6 @@ class ZHADeviceNeighbors extends LitElement {
.columns=${this._columns(this.narrow)}
.data=${this._deviceNeighbors(this.device, this._devices)}
auto-height
.dir=${computeRTLDirection(this.hass)}
.searchLabel=${this.hass.localize(
"ui.components.data-table.search"
)}

View File

@ -18,7 +18,6 @@ import {
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { computeRTL } from "../../../../../common/util/compute_rtl";
import "../../../../../components/ha-card";
import "../../../../../components/ha-expansion-panel";
import "../../../../../components/ha-fab";
@ -472,7 +471,6 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
"ui.panel.config.zwave_js.common.add_node"
)}
extended
?rtl=${computeRTL(this.hass)}
@click=${this._addNodeClicked}
.disabled=${this._status !== "connected" ||
(this._network?.controller.inclusion_state !== InclusionState.Idle &&

View File

@ -26,7 +26,6 @@ import { relativeTime } from "../../../common/datetime/relative_time";
import { HASSDomEvent, fireEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { navigate } from "../../../common/navigate";
import { computeRTL } from "../../../common/util/compute_rtl";
import {
DataTableColumnContainer,
RowClickedEvent,
@ -308,7 +307,6 @@ class HaScriptPicker extends LitElement {
"ui.panel.config.script.picker.add_script"
)}
extended
?rtl=${computeRTL(this.hass)}
@click=${this._createNew}
>
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>

View File

@ -8,7 +8,6 @@ import {
nothing,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import "../../../components/ha-circular-progress";
import { createCloseHeading } from "../../../components/ha-dialog";
import "../../../components/ha-formfield";
@ -161,7 +160,6 @@ export class DialogAddUser extends LitElement {
.label=${this.hass.localize(
"ui.panel.config.users.editor.local_only"
)}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.checked=${this._localOnly}
@ -173,7 +171,6 @@ export class DialogAddUser extends LitElement {
<div class="row">
<ha-formfield
.label=${this.hass.localize("ui.panel.config.users.editor.admin")}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.checked=${this._isAdmin}

View File

@ -3,7 +3,6 @@ import "@material/mwc-button";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { createCloseHeading } from "../../../components/ha-dialog";
import "../../../components/ha-formfield";
import "../../../components/ha-help-tooltip";
@ -99,7 +98,6 @@ class DialogUserDetail extends LitElement {
.label=${this.hass.localize(
"ui.panel.config.users.editor.local_only"
)}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.disabled=${user.system_generated}
@ -114,7 +112,6 @@ class DialogUserDetail extends LitElement {
.label=${this.hass.localize(
"ui.panel.config.users.editor.admin"
)}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.disabled=${user.system_generated || user.is_owner}
@ -137,7 +134,6 @@ class DialogUserDetail extends LitElement {
.label=${this.hass.localize(
"ui.panel.config.users.editor.active"
)}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.disabled=${user.system_generated || user.is_owner}

View File

@ -225,14 +225,12 @@ export class AssistPref extends LitElement {
.header-actions {
position: absolute;
right: 0px;
inset-inline-end: 0px;
inset-inline-start: initial;
top: 24px;
display: flex;
flex-direction: row;
}
:host([dir="rtl"]) .header-actions {
right: auto;
left: 0;
}
.header-actions .icon-link {
margin-top: -16px;
margin-right: 8px;

View File

@ -258,14 +258,12 @@ export class CloudAlexaPref extends LitElement {
.header-actions {
position: absolute;
right: 24px;
inset-inline-end: 24px;
inset-inline-start: initial;
top: 24px;
display: flex;
flex-direction: row;
}
:host([dir="rtl"]) .header-actions {
right: auto;
left: 24px;
}
.header-actions .icon-link {
margin-top: -16px;
margin-right: 8px;

View File

@ -327,14 +327,12 @@ export class CloudGooglePref extends LitElement {
.header-actions {
position: absolute;
right: 24px;
inset-inline-end: 24px;
inset-inline-start: initial;
top: 24px;
display: flex;
flex-direction: row;
}
:host([dir="rtl"]) .header-actions {
right: auto;
left: 24px;
}
.header-actions .icon-link {
margin-top: -16px;
margin-right: 8px;

View File

@ -1,7 +1,6 @@
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { CloudStatus } from "../../../data/cloud";
import { ExposeEntitySettings } from "../../../data/expose";
@ -48,7 +47,6 @@ export class HaConfigVoiceAssistantsAssistants extends LitElement {
${isComponentLoaded(this.hass, "assist_pipeline")
? html`
<assist-pref
.dir=${computeRTLDirection(this.hass)}
.hass=${this.hass}
.cloudStatus=${this.cloudStatus}
.exposedEntities=${this.exposedEntities}
@ -61,13 +59,11 @@ export class HaConfigVoiceAssistantsAssistants extends LitElement {
.hass=${this.hass}
.exposedEntities=${this.exposedEntities}
.cloudStatus=${this.cloudStatus}
.dir=${computeRTLDirection(this.hass)}
></cloud-alexa-pref>
<cloud-google-pref
.hass=${this.hass}
.exposedEntities=${this.exposedEntities}
.cloudStatus=${this.cloudStatus}
.dir=${computeRTLDirection(this.hass)}
></cloud-google-pref>
`
: html`<cloud-discover .hass=${this.hass}></cloud-discover>`}

View File

@ -27,7 +27,6 @@ import {
isEmptyFilter,
} from "../../../common/entity/entity_filter";
import { navigate } from "../../../common/navigate";
import { computeRTL } from "../../../common/util/compute_rtl";
import {
DataTableColumnContainer,
DataTableRowData,
@ -618,7 +617,6 @@ export class VoiceAssistantsExpose extends LitElement {
"ui.panel.config.voice_assistants.expose.add"
)}
extended
?rtl=${computeRTL(this.hass)}
@click=${this._addEntry}
>
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>

View File

@ -588,13 +588,10 @@ class HaPanelDevService extends LitElement {
}
.attributes th {
text-align: left;
text-align: var(--float-start);
background-color: var(--card-background-color);
border-bottom: 1px solid var(--primary-text-color);
}
:host([rtl]) .attributes th {
text-align: right;
direction: var(--direction);
}
.attributes tr {

View File

@ -16,9 +16,7 @@ import memoizeOne from "memoize-one";
import { formatDateTimeWithSeconds } from "../../../common/datetime/format_date_time";
import { storage } from "../../../common/decorators/storage";
import { fireEvent } from "../../../common/dom/fire_event";
import { toggleAttribute } from "../../../common/dom/toggle_attribute";
import { escapeRegExp } from "../../../common/string/escape_regexp";
import { computeRTL } from "../../../common/util/compute_rtl";
import { copyToClipboard } from "../../../common/util/copy-clipboard";
import "../../../components/entity/ha-entity-picker";
import "../../../components/ha-alert";
@ -69,8 +67,6 @@ class HaPanelDevState extends LitElement {
@property({ type: Boolean, reflect: true }) public narrow = false;
@property({ type: Boolean, reflect: true }) public rtl = false;
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;
private _filteredEntities = memoizeOne(
@ -325,14 +321,6 @@ class HaPanelDevState extends LitElement {
`;
}
protected updated(changedProps) {
super.updated(changedProps);
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass.locale) {
toggleAttribute(this, "rtl", computeRTL(this.hass));
}
}
private _copyEntity(ev) {
ev.preventDefault();
const entity = (ev.currentTarget! as any).entity;
@ -626,7 +614,8 @@ class HaPanelDevState extends LitElement {
.entities th {
padding: 0 8px;
text-align: left;
text-align: var(--float-start);
direction: var(--direction);
}
.filters th {
@ -652,15 +641,6 @@ class HaPanelDevState extends LitElement {
bottom: -8px;
}
:host([rtl]) .entities th {
text-align: right;
direction: rtl;
}
:host([rtl]) .filters {
direction: rtl;
}
.entities tr {
vertical-align: top;
direction: ltr;

View File

@ -1,7 +1,7 @@
import "@material/mwc-button/mwc-button";
import { mdiSlopeUphill } from "@mdi/js";
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement } from "lit";
import { CSSResultGroup, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
@ -26,7 +26,6 @@ import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import { showStatisticsAdjustSumDialog } from "./show-dialog-statistics-adjust-sum";
import { showFixStatisticsUnitsChangedDialog } from "./show-dialog-statistics-fix-units-changed";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { documentationUrl } from "../../../util/documentation-url";
const FIX_ISSUES_ORDER = {
@ -183,7 +182,6 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
id="statistic_id"
clickable
@row-click=${this._rowClicked}
.dir=${computeRTLDirection(this.hass)}
></ha-data-table>
`;
}
@ -411,45 +409,7 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
};
static get styles(): CSSResultGroup {
return [
haStyle,
css`
.content {
padding: 16px;
padding: max(16px, env(safe-area-inset-top))
max(16px, env(safe-area-inset-right))
max(16px, env(safe-area-inset-bottom))
max(16px, env(safe-area-inset-left));
}
th {
padding: 0 8px;
text-align: left;
}
:host([rtl]) th {
text-align: right;
}
tr {
vertical-align: top;
direction: ltr;
}
tr:nth-child(odd) {
background-color: var(--table-row-background-color, #fff);
}
tr:nth-child(even) {
background-color: var(--table-row-alternative-background-color, #eee);
}
td {
padding: 4px;
min-width: 200px;
word-break: break-word;
}
`,
];
return haStyle;
}
}

View File

@ -1,5 +1,4 @@
import "@material/mwc-button/mwc-button";
import "@material/mwc-list/mwc-list-item";
import formatISO9075 from "date-fns/formatISO9075";
import {
css,
@ -21,6 +20,7 @@ import "../../../components/ha-selector/ha-selector-datetime";
import "../../../components/ha-selector/ha-selector-number";
import "../../../components/ha-svg-icon";
import "../../../components/ha-icon-next";
import "../../../components/ha-list-item";
import {
adjustStatisticsSum,
fetchStatistics,
@ -151,7 +151,7 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement {
const stat = data[i];
const growth = Math.round(stat.change! * 100) / 100;
rows.push(html`
<mwc-list-item
<ha-list-item
twoline
hasMeta
.stat=${stat}
@ -166,7 +166,7 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement {
)}
</span>
<ha-icon-next slot="meta"></ha-icon-next>
</mwc-list-item>
</ha-list-item>
`);
}
stats = html`${rows}`;
@ -413,7 +413,7 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement {
ha-selector-number {
margin-bottom: 20px;
}
mwc-list-item {
ha-list-item {
margin: 0 -24px;
--mdc-list-side-padding: 24px;
}

View File

@ -1,4 +1,4 @@
import { mdiDownload, mdiFilterRemove, mdiRefresh } from "@mdi/js";
import { mdiDownload, mdiFilterRemove } from "@mdi/js";
import { differenceInHours } from "date-fns/esm";
import {
HassServiceTarget,
@ -6,6 +6,7 @@ import {
} from "home-assistant-js-websocket/dist/types";
import { LitElement, PropertyValues, css, html } from "lit";
import { property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { ensureArray } from "../../common/array/ensure-array";
import { storage } from "../../common/decorators/storage";
import { navigate } from "../../common/navigate";
@ -15,7 +16,6 @@ import {
extractSearchParamsObject,
removeSearchParam,
} from "../../common/url/search-params";
import { computeRTL } from "../../common/util/compute_rtl";
import { MIN_TIME_BETWEEN_UPDATES } from "../../components/chart/ha-chart-base";
import "../../components/chart/state-history-charts";
import type { StateHistoryCharts } from "../../components/chart/state-history-charts";
@ -45,8 +45,8 @@ import {
HistoryStates,
EntityHistoryState,
LineChartUnit,
LineChartEntity,
computeGroupKey,
LineChartState,
} from "../../data/history";
import { fetchStatistics, Statistics } from "../../data/recorder";
import { getSensorNumericDeviceClasses } from "../../data/sensor";
@ -54,6 +54,7 @@ import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { haStyle } from "../../resources/styles";
import { HomeAssistant } from "../../types";
import { fileDownload } from "../../util/file_download";
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
class HaPanelHistory extends SubscribeMixin(LitElement) {
@property({ attribute: false }) hass!: HomeAssistant;
@ -71,7 +72,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
state: true,
subscribe: false,
})
private _targetPickerValue?: HassServiceTarget;
private _targetPickerValue: HassServiceTarget = {};
@state() private _isLoading = false;
@ -138,6 +139,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
}
protected render() {
const entitiesSelected = this._getEntityIds().length > 0;
return html`
<ha-top-app-bar-fixed>
${this._showBack
@ -155,7 +157,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
></ha-menu-button>
`}
<div slot="title">${this.hass.localize("panel.history")}</div>
${this._targetPickerValue
${entitiesSelected
? html`
<ha-icon-button
slot="actionItems"
@ -166,17 +168,10 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
></ha-icon-button>
`
: ""}
<ha-icon-button
slot="actionItems"
@click=${this._getHistory}
.disabled=${this._isLoading || !this._targetPickerValue}
.path=${mdiRefresh}
.label=${this.hass.localize("ui.common.refresh")}
></ha-icon-button>
<ha-icon-button
slot="actionItems"
@click=${this._downloadHistory}
.disabled=${this._isLoading || !this._targetPickerValue}
.disabled=${this._isLoading}
.path=${mdiDownload}
.label=${this.hass.localize("ui.panel.history.download_data")}
></ha-icon-button>
@ -203,7 +198,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
? html`<div class="progress-wrapper">
<ha-circular-progress indeterminate></ha-circular-progress>
</div>`
: !this._targetPickerValue
: !entitiesSelected
? html`<div class="start-search">
${this.hass.localize("ui.panel.history.start_search")}
</div>`
@ -227,50 +222,83 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
): HistoryResult {
const result: HistoryResult = { ...historyResult, line: [] };
const keys = new Set(
historyResult.line
.map((i) => computeGroupKey(i.unit, i.device_class, true))
.concat(
ltsResult.line.map((i) =>
computeGroupKey(i.unit, i.device_class, true)
)
)
);
keys.forEach((key) => {
const historyItem = historyResult.line.find(
(i) => computeGroupKey(i.unit, i.device_class, true) === key
);
const ltsItem = ltsResult.line.find(
(i) => computeGroupKey(i.unit, i.device_class, true) === key
);
if (historyItem && ltsItem) {
const newLineItem: LineChartUnit = { ...historyItem, data: [] };
const entities = new Set(
historyItem.data
.map((d) => d.entity_id)
.concat(ltsItem.data.map((d) => d.entity_id))
);
entities.forEach((entity) => {
const historyDataItem = historyItem.data.find(
(d) => d.entity_id === entity
);
const ltsDataItem = ltsItem.data.find((d) => d.entity_id === entity);
if (historyDataItem && ltsDataItem) {
const newDataItem: LineChartEntity = {
...historyDataItem,
statistics: ltsDataItem.statistics,
};
newLineItem.data.push(newDataItem);
} else {
newLineItem.data.push(historyDataItem || ltsDataItem!);
}
});
result.line.push(newLineItem);
const lookup: Record<
string,
{ historyItem?: LineChartUnit; ltsItem?: LineChartUnit }
> = {};
for (const item of historyResult.line) {
const key = computeGroupKey(item.unit, item.device_class, true);
if (key) {
lookup[key] = {
historyItem: item,
};
}
}
for (const item of ltsResult.line) {
const key = computeGroupKey(item.unit, item.device_class, true);
if (!key) {
continue;
}
if (key in lookup) {
lookup[key].ltsItem = item;
} else {
lookup[key] = { ltsItem: item };
}
}
for (const { historyItem, ltsItem } of Object.values(lookup)) {
if (!historyItem || !ltsItem) {
// Only one result has data for this item, so just push it directly instead of merging.
result.line.push(historyItem || ltsItem!);
continue;
}
});
const newLineItem: LineChartUnit = { ...historyItem, data: [] };
const entities = new Set([
...historyItem.data.map((d) => d.entity_id),
...ltsItem.data.map((d) => d.entity_id),
]);
for (const entity of entities) {
const historyDataItem = historyItem.data.find(
(d) => d.entity_id === entity
);
const ltsDataItem = ltsItem.data.find((d) => d.entity_id === entity);
if (!historyDataItem || !ltsDataItem) {
newLineItem.data.push(historyDataItem || ltsDataItem!);
continue;
}
// Remove statistics that overlap with states
const oldestState =
historyDataItem.states[0]?.last_changed ||
// If no state, fall back to the max last changed of the last statistics (so approve all)
ltsDataItem.statistics![ltsDataItem.statistics!.length - 1]
.last_changed + 1;
const statistics: LineChartState[] = [];
for (const s of ltsDataItem.statistics!) {
if (s.last_changed >= oldestState) {
break;
}
statistics.push(s);
}
newLineItem.data.push(
statistics.length === 0
? // All statistics overlapped with states, so just push the states
historyDataItem
: {
...historyDataItem,
statistics,
}
);
}
result.line.push(newLineItem);
}
return result;
}
@ -342,114 +370,90 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
protected updated(changedProps: PropertyValues) {
if (
this._targetPickerValue &&
(changedProps.has("_startDate") ||
changedProps.has("_endDate") ||
changedProps.has("_targetPickerValue") ||
(!this._stateHistory &&
(changedProps.has("_deviceEntityLookup") ||
changedProps.has("_areaEntityLookup") ||
changedProps.has("_areaDeviceLookup"))))
changedProps.has("_startDate") ||
changedProps.has("_endDate") ||
changedProps.has("_targetPickerValue") ||
(!this._stateHistory &&
(changedProps.has("_deviceEntityLookup") ||
changedProps.has("_areaEntityLookup") ||
changedProps.has("_areaDeviceLookup")))
) {
this._getHistory();
this._getStats();
}
if (!changedProps.has("hass") && !changedProps.has("_entities")) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.language !== this.hass.language) {
this.rtl = computeRTL(this.hass);
}
}
private _removeAll() {
this._targetPickerValue = undefined;
this._targetPickerValue = {};
this._updatePath();
}
private async _getStats() {
const entityIds = this._getEntityIds();
if (!entityIds) {
const statisticIds = this._getEntityIds();
if (statisticIds.length === 0) {
this._statisticsHistory = undefined;
return;
}
this._getStatistics(entityIds);
}
private async _getStatistics(statisticIds: string[]): Promise<void> {
try {
const statistics = await fetchStatistics(
this.hass!,
this._startDate,
this._endDate,
statisticIds,
"hour",
undefined,
["mean", "state"]
);
const statistics = await fetchStatistics(
this.hass!,
this._startDate,
this._endDate,
statisticIds,
"hour",
undefined,
["mean", "state"]
);
// Maintain the statistic id ordering
const orderedStatistics: Statistics = {};
statisticIds.forEach((id) => {
if (id in statistics) {
orderedStatistics[id] = statistics[id];
}
// Maintain the statistic id ordering
const orderedStatistics: Statistics = {};
statisticIds.forEach((id) => {
if (id in statistics) {
orderedStatistics[id] = statistics[id];
}
});
// Convert statistics to HistoryResult format
const statsHistoryStates: HistoryStates = {};
Object.entries(orderedStatistics).forEach(([key, value]) => {
const entityHistoryStates: EntityHistoryState[] = value.map((e) => ({
s: e.mean != null ? e.mean.toString() : e.state!.toString(),
lc: e.start / 1000,
a: {},
lu: e.start / 1000,
}));
statsHistoryStates[key] = entityHistoryStates;
});
const { numeric_device_classes: sensorNumericDeviceClasses } =
await getSensorNumericDeviceClasses(this.hass);
this._statisticsHistory = computeHistory(
this.hass,
statsHistoryStates,
this.hass.localize,
sensorNumericDeviceClasses,
true
);
// remap states array to statistics array
(this._statisticsHistory?.line || []).forEach((item) => {
item.data.forEach((data) => {
data.statistics = data.states;
data.states = [];
});
// Convert statistics to HistoryResult format
const statsHistoryStates: HistoryStates = {};
Object.entries(orderedStatistics).forEach(([key, value]) => {
const entityHistoryStates: EntityHistoryState[] = value.map((e) => ({
s: e.mean != null ? e.mean.toString() : e.state!.toString(),
lc: e.start / 1000,
a: {},
lu: e.start / 1000,
}));
statsHistoryStates[key] = entityHistoryStates;
});
const { numeric_device_classes: sensorNumericDeviceClasses } =
await getSensorNumericDeviceClasses(this.hass);
this._statisticsHistory = computeHistory(
this.hass,
statsHistoryStates,
this.hass.localize,
sensorNumericDeviceClasses,
true
);
// remap states array to statistics array
(this._statisticsHistory?.line || []).forEach((item) => {
item.data.forEach((data) => {
data.statistics = data.states;
data.states = [];
});
});
} catch (err) {
this._statisticsHistory = undefined;
}
});
}
private async _getHistory() {
if (!this._targetPickerValue) {
return;
}
this._isLoading = true;
const entityIds = this._getEntityIds();
if (entityIds === undefined) {
this._isLoading = false;
if (entityIds.length === 0) {
this._stateHistory = undefined;
return;
}
if (entityIds.length === 0) {
this._isLoading = false;
this._stateHistory = { line: [], timeline: [] };
return;
}
this._isLoading = true;
if (this._subscribed) {
this._unsubscribeHistory();
@ -512,84 +516,100 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
}
}
private _getEntityIds(): string[] | undefined {
if (
!this._targetPickerValue ||
this._deviceEntityLookup === undefined ||
this._areaEntityLookup === undefined ||
this._areaDeviceLookup === undefined
) {
return undefined;
}
private _getEntityIds(): string[] {
return this.__getEntityIds(
this._targetPickerValue,
this._deviceEntityLookup,
this._areaEntityLookup,
this._areaDeviceLookup
);
}
const entityIds = new Set<string>();
let {
area_id: searchingAreaId,
device_id: searchingDeviceId,
entity_id: searchingEntityId,
} = this._targetPickerValue;
private __getEntityIds = memoizeOne(
(
targetPickerValue: HassServiceTarget,
deviceEntityLookup: DeviceEntityLookup | undefined,
areaEntityLookup: AreaEntityLookup | undefined,
areaDeviceLookup: AreaDeviceLookup | undefined
): string[] => {
if (
!targetPickerValue ||
deviceEntityLookup === undefined ||
areaEntityLookup === undefined ||
areaDeviceLookup === undefined
) {
return [];
}
const entityIds = new Set<string>();
let {
area_id: searchingAreaId,
device_id: searchingDeviceId,
entity_id: searchingEntityId,
} = targetPickerValue;
if (searchingAreaId) {
searchingAreaId = ensureArray(searchingAreaId);
for (const singleSearchingAreaId of searchingAreaId) {
const foundEntities = areaEntityLookup[singleSearchingAreaId];
if (foundEntities?.length) {
for (const foundEntity of foundEntities) {
if (foundEntity.entity_category === null) {
entityIds.add(foundEntity.entity_id);
}
}
}
const foundDevices = areaDeviceLookup[singleSearchingAreaId];
if (!foundDevices?.length) {
continue;
}
for (const foundDevice of foundDevices) {
const foundDeviceEntities = deviceEntityLookup[foundDevice.id];
if (!foundDeviceEntities?.length) {
continue;
}
for (const foundDeviceEntity of foundDeviceEntities) {
if (
(!foundDeviceEntity.area_id ||
foundDeviceEntity.area_id === singleSearchingAreaId) &&
foundDeviceEntity.entity_category === null
) {
entityIds.add(foundDeviceEntity.entity_id);
}
}
}
}
}
if (searchingDeviceId) {
searchingDeviceId = ensureArray(searchingDeviceId);
for (const singleSearchingDeviceId of searchingDeviceId) {
const foundEntities = deviceEntityLookup[singleSearchingDeviceId];
if (!foundEntities?.length) {
continue;
}
if (searchingAreaId) {
searchingAreaId = ensureArray(searchingAreaId);
for (const singleSearchingAreaId of searchingAreaId) {
const foundEntities = this._areaEntityLookup[singleSearchingAreaId];
if (foundEntities?.length) {
for (const foundEntity of foundEntities) {
if (foundEntity.entity_category === null) {
entityIds.add(foundEntity.entity_id);
}
}
}
}
const foundDevices = this._areaDeviceLookup[singleSearchingAreaId];
if (!foundDevices?.length) {
continue;
}
for (const foundDevice of foundDevices) {
const foundDeviceEntities = this._deviceEntityLookup[foundDevice.id];
if (!foundDeviceEntities?.length) {
continue;
}
for (const foundDeviceEntity of foundDeviceEntities) {
if (
(!foundDeviceEntity.area_id ||
foundDeviceEntity.area_id === singleSearchingAreaId) &&
foundDeviceEntity.entity_category === null
) {
entityIds.add(foundDeviceEntity.entity_id);
}
}
if (searchingEntityId) {
searchingEntityId = ensureArray(searchingEntityId);
for (const singleSearchingEntityId of searchingEntityId) {
entityIds.add(singleSearchingEntityId);
}
}
return [...entityIds];
}
if (searchingDeviceId) {
searchingDeviceId = ensureArray(searchingDeviceId);
for (const singleSearchingDeviceId of searchingDeviceId) {
const foundEntities = this._deviceEntityLookup[singleSearchingDeviceId];
if (!foundEntities?.length) {
continue;
}
for (const foundEntity of foundEntities) {
if (foundEntity.entity_category === null) {
entityIds.add(foundEntity.entity_id);
}
}
}
}
if (searchingEntityId) {
searchingEntityId = ensureArray(searchingEntityId);
for (const singleSearchingEntityId of searchingEntityId) {
entityIds.add(singleSearchingEntityId);
}
}
return [...entityIds];
}
);
private _dateRangeChanged(ev) {
this._startDate = ev.detail.startDate;
@ -611,20 +631,18 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
private _updatePath() {
const params: Record<string, string> = {};
if (this._targetPickerValue) {
if (this._targetPickerValue.entity_id) {
params.entity_id = ensureArray(this._targetPickerValue.entity_id).join(
","
);
}
if (this._targetPickerValue.area_id) {
params.area_id = ensureArray(this._targetPickerValue.area_id).join(",");
}
if (this._targetPickerValue.device_id) {
params.device_id = ensureArray(this._targetPickerValue.device_id).join(
","
);
}
if (this._targetPickerValue.entity_id) {
params.entity_id = ensureArray(this._targetPickerValue.entity_id).join(
","
);
}
if (this._targetPickerValue.area_id) {
params.area_id = ensureArray(this._targetPickerValue.area_id).join(",");
}
if (this._targetPickerValue.device_id) {
params.device_id = ensureArray(this._targetPickerValue.device_id).join(
","
);
}
if (this._startDate) {
@ -639,23 +657,35 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
}
private _downloadHistory() {
const entities = this._getEntityIds();
if (entities.length === 0 || !this._mungedStateHistory) {
showAlertDialog(this, {
title: this.hass.localize("ui.panel.history.download_data_error"),
text: this.hass.localize("ui.panel.history.error_no_data"),
warning: true,
});
return;
}
const csv: string[] = ["entity_id,state,last_changed\n"];
const formatDate = (number) => new Date(number).toISOString();
for (const line of this._mungedStateHistory!.line) {
for (const line of this._mungedStateHistory.line) {
for (const entity of line.data) {
const entityId = entity.entity_id;
for (const data of [entity.states, entity.statistics]) {
if (!data) {
continue;
}
for (const s of data) {
if (entity.statistics) {
for (const s of entity.statistics) {
csv.push(`${entityId},${s.state},${formatDate(s.last_changed)}\n`);
}
}
for (const s of entity.states) {
csv.push(`${entityId},${s.state},${formatDate(s.last_changed)}\n`);
}
}
}
for (const timeline of this._mungedStateHistory!.timeline) {
for (const timeline of this._mungedStateHistory.timeline) {
const entityId = timeline.entity_id;
for (const s of timeline.data) {
csv.push(`${entityId},${s.state},${formatDate(s.last_changed)}\n`);
@ -710,6 +740,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
ha-date-range-picker {
margin-right: 0;
margin-inline-end: 0;
margin-inline-start: initial;
width: 100%;
}
}

View File

@ -215,6 +215,8 @@ class HuiEnergyCarbonGaugeCard
ha-svg-icon {
position: absolute;
right: 4px;
inset-inline-end: 4px;
inset-inline-start: initial;
top: 4px;
color: var(--secondary-text-color);
}

View File

@ -192,6 +192,8 @@ class HuiEnergyGridGaugeCard
ha-svg-icon {
position: absolute;
right: 4px;
inset-inline-end: 4px;
inset-inline-start: initial;
top: 4px;
color: var(--secondary-text-color);
}

View File

@ -255,6 +255,8 @@ class HuiEnergySelfSufficiencyGaugeCard
ha-svg-icon {
position: absolute;
right: 4px;
inset-inline-end: 4px;
inset-inline-start: initial;
top: 4px;
color: var(--secondary-text-color);
}

View File

@ -194,6 +194,8 @@ class HuiEnergySolarGaugeCard
ha-svg-icon {
position: absolute;
right: 4px;
inset-inline-end: 4px;
inset-inline-start: initial;
top: 4px;
color: var(--secondary-text-color);
}

View File

@ -187,8 +187,6 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
return html`
<ha-card
@action=${this._handleAction}
@focus=${this.handleRippleFocus}
@blur=${this.handleRippleBlur}
@mousedown=${this.handleRippleActivate}
@mouseup=${this.handleRippleDeactivate}
@mouseenter=${this.handleRippleMouseEnter}
@ -289,16 +287,6 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
this._rippleHandlers.endPress();
}
@eventOptions({ passive: true })
private handleRippleFocus() {
this._rippleHandlers.startFocus();
}
@eventOptions({ passive: true })
private handleRippleBlur() {
this._rippleHandlers.endFocus();
}
@eventOptions({ passive: true })
private handleRippleMouseEnter() {
this._rippleHandlers.startHover();
@ -352,7 +340,18 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
outline: none;
}
:host(:focus-visible) ha-state-icon,
ha-card:focus-visible {
--shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent);
--shadow-focus: 0 0 0 1px
var(--state-color, var(--paper-item-icon-color, #44739e));
border-color: var(
--state-color,
var(--paper-item-icon-color, #44739e)
);
box-shadow: var(--shadow-default), var(--shadow-focus);
}
ha-card:focus-visible ha-state-icon,
:host(:active) ha-state-icon {
transform: scale(1.2);
}

View File

@ -1,5 +1,4 @@
import { mdiImageFilterCenterFocus } from "@mdi/js";
import { isToday } from "date-fns";
import { HassEntities } from "home-assistant-js-websocket";
import { LatLngTuple } from "leaflet";
import {
@ -14,12 +13,8 @@ import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { getColorByIndex } from "../../../common/color/colors";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { formatDateTime } from "../../../common/datetime/format_date_time";
import {
formatTimeWithSeconds,
formatTimeWeekday,
} from "../../../common/datetime/format_time";
import { computeDomain } from "../../../common/entity/compute_domain";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { deepEqual } from "../../../common/util/deep-equal";
import parseAspectRatio from "../../../common/util/parse-aspect-ratio";
import "../../../components/ha-card";
@ -391,28 +386,23 @@ class HuiMapCard extends LitElement implements LovelaceCard {
}
const p = {} as HaMapPathPoint;
p.point = [latitude, longitude] as LatLngTuple;
const t = new Date(entityState.lu * 1000);
if ((config.hours_to_show! ?? DEFAULT_HOURS_TO_SHOW) > 144) {
// if showing > 6 days in the history trail, show the full
// date and time
p.tooltip = formatDateTime(t, this.hass.locale, this.hass.config);
} else if (isToday(t)) {
p.tooltip = formatTimeWithSeconds(
t,
this.hass.locale,
this.hass.config
);
} else {
p.tooltip = formatTimeWeekday(
t,
this.hass.locale,
this.hass.config
);
}
p.timestamp = new Date(entityState.lu * 1000);
points.push(p);
}
const entityConfig = this._configEntities?.find(
(e) => e.entity === entityId
);
const name =
entityConfig?.name ??
(entityId in this.hass.states
? computeStateName(this.hass.states[entityId])
: entityId);
paths.push({
points,
name,
fullDatetime: (config.hours_to_show ?? DEFAULT_HOURS_TO_SHOW) > 144,
color: this._getColor(entityId),
gradualOpacity: 0.8,
});

View File

@ -524,6 +524,8 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
position: absolute;
top: -3px;
right: -3px;
inset-inline-end: -3px;
inset-inline-start: initial;
}
.icon-container:not([role="button"]) {
pointer-events: none;

View File

@ -612,11 +612,6 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
text-align: center;
}
/* ============= RTL ============= */
:host([rtl]) .temp-attribute {
text-align: left;
}
/* ============= NARROW ============= */
:host([narrow]) .icon-image {

View File

@ -13,7 +13,6 @@ import { DOMAINS_INPUT_ROW } from "../../../common/const";
import { toggleAttribute } from "../../../common/dom/toggle_attribute";
import { computeDomain } from "../../../common/entity/compute_domain";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { computeRTL } from "../../../common/util/compute_rtl";
import "../../../components/entity/state-badge";
import "../../../components/ha-relative-time";
import { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
@ -186,9 +185,6 @@ export class HuiGenericEntityRow extends LitElement {
"no-secondary",
!this.secondaryText && !this.config?.secondary_info
);
if (changedProps.has("hass")) {
toggleAttribute(this, "rtl", computeRTL(this.hass!));
}
}
private _handleAction(ev: ActionHandlerEvent) {
@ -233,22 +229,11 @@ export class HuiGenericEntityRow extends LitElement {
state-badge {
flex: 0 0 40px;
}
:host([rtl]) .flex {
margin-left: 0;
margin-right: 16px;
}
:host([rtl]) .flex ::slotted(*) {
margin-left: 0;
margin-right: 8px;
}
.pointer {
cursor: pointer;
}
.state {
text-align: right;
}
.state.rtl {
text-align: left;
text-align: var(--float-end);
}
.value {
direction: ltr;

View File

@ -1,6 +1,5 @@
import { PropertyValues, ReactiveElement } from "lit";
import { property } from "lit/decorators";
import { computeRTL } from "../../../../common/util/compute_rtl";
import { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import { HomeAssistant } from "../../../../types";
import { createCardElement } from "../../create-element/create-card-element";
@ -70,13 +69,6 @@ export class HuiCardPreview extends ReactiveElement {
}
if (changedProperties.has("hass")) {
const oldHass = changedProperties.get("hass") as
| HomeAssistant
| undefined;
if (!oldHass || oldHass.language !== this.hass!.language) {
this.style.direction = computeRTL(this.hass!) ? "rtl" : "ltr";
}
if (this._element) {
this._element.hass = this.hass;
}

View File

@ -3,7 +3,6 @@ import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import "../../../../components/data-table/ha-data-table";
import type {
DataTableColumnContainer,
@ -33,7 +32,6 @@ export class HuiEntityPickerTable extends LitElement {
.id=${"entity_id"}
.columns=${this._columns(this.narrow!)}
.data=${this.entities}
.dir=${computeRTLDirection(this.hass)}
.searchLabel=${this.hass.localize(
"ui.panel.lovelace.unused_entities.search"
)}

View File

@ -18,7 +18,6 @@ import {
} from "superstruct";
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
import { customType } from "../../../../common/structs/is-custom-type";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import "../../../../components/ha-card";
import "../../../../components/ha-formfield";
import "../../../../components/ha-icon";
@ -265,7 +264,6 @@ export class HuiEntitiesCardEditor
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.entities.show_header_toggle"
)}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.checked=${this._config!.show_header_toggle !== false}
@ -277,7 +275,6 @@ export class HuiEntitiesCardEditor
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.state_color"
)}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.checked=${this._config!.state_color}

View File

@ -1,4 +1,3 @@
import { computeRTL } from "../../../common/util/compute_rtl";
import "../../../components/entity/ha-state-label-badge";
import { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
import { HomeAssistant } from "../../../types";
@ -29,10 +28,6 @@ export class HuiBadgePreview extends HTMLElement {
}
set hass(hass: HomeAssistant) {
if (!this._hass || this._hass.language !== hass.language) {
this.style.direction = computeRTL(hass) ? "rtl" : "ltr";
}
this._hass = hass;
if (this._element) {
this._element.hass = hass;

View File

@ -3,7 +3,6 @@ import { mdiHelpCircle } from "@mdi/js";
import { CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import "../../../components/ha-circular-progress";
import "../../../components/ha-dialog";
import "../../../components/ha-formfield";
@ -64,7 +63,6 @@ export class HuiSaveConfig extends LitElement implements HassDialog {
title=${this.hass!.localize("ui.panel.lovelace.menu.help")}
target="_blank"
rel="noreferrer"
dir=${computeRTLDirection(this.hass!)}
>
<ha-icon-button
.path=${mdiHelpCircle}
@ -88,7 +86,6 @@ export class HuiSaveConfig extends LitElement implements HassDialog {
.label=${this.hass!.localize(
"ui.panel.lovelace.editor.save_config.empty_config"
)}
.dir=${computeRTLDirection(this.hass!)}
>
<ha-switch
.checked=${this._emptyConfig}

View File

@ -11,7 +11,6 @@ import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { computeDomain } from "../../../../common/entity/compute_domain";
import { computeStateName } from "../../../../common/entity/compute_state_name";
import { computeRTL } from "../../../../common/util/compute_rtl";
import type { DataTableRowData } from "../../../../components/data-table/ha-data-table";
import "../../../../components/ha-fab";
import "../../../../components/ha-svg-icon";
@ -99,7 +98,6 @@ export class HuiUnusedEntities extends LitElement {
</div>
<div
class="fab ${classMap({
rtl: computeRTL(this.hass),
selected: this._selectedEntities.length,
})}"
>
@ -171,20 +169,13 @@ export class HuiUnusedEntities extends LitElement {
}
.fab {
position: sticky;
float: right;
float: var(--float-end);
right: calc(16px + env(safe-area-inset-right));
bottom: calc(16px + env(safe-area-inset-bottom));
inset-inline-end: calc(16px + env(safe-area-inset-right));
inset-inline-start: initial;
z-index: 1;
}
.fab.rtl {
right: initial;
left: 0;
bottom: 0;
padding-right: 16px;
padding-left: calc(16px + env(safe-area-inset-left));
padding-inline-end: 16px;
padding-inline-start: calc(16px + env(safe-area-inset-left));
}
ha-fab {
position: relative;
bottom: calc(-80px - env(safe-area-inset-bottom));

View File

@ -7,7 +7,6 @@ import {
nothing,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { debounce } from "../../../common/util/debounce";
import "../../../components/ha-slider";
import "../../../components/ha-textfield";
@ -86,7 +85,6 @@ class HuiInputNumberEntityRow extends LitElement implements LovelaceRow {
<div class="flex">
<ha-slider
labeled
.dir=${computeRTLDirection(this.hass)}
.disabled=${isUnavailableState(stateObj.state)}
.step=${Number(stateObj.attributes.step)}
.min=${Number(stateObj.attributes.min)}

View File

@ -23,7 +23,6 @@ import {
import { customElement, property, state } from "lit/decorators";
import { stateActive } from "../../../common/entity/state_active";
import { supportsFeature } from "../../../common/entity/supports-feature";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { debounce } from "../../../common/util/debounce";
import "../../../components/ha-icon-button";
import "../../../components/ha-slider";
@ -254,7 +253,6 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
? html`
<ha-slider
labeled
.dir=${computeRTLDirection(this.hass!)}
.value=${Number(stateObj.attributes.volume_level) * 100}
@change=${this._selectedValueChanged}
id="input"

View File

@ -7,7 +7,6 @@ import {
nothing,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { debounce } from "../../../common/util/debounce";
import "../../../components/ha-slider";
import "../../../components/ha-textfield";
@ -91,7 +90,6 @@ class HuiNumberEntityRow extends LitElement implements LovelaceRow {
<ha-slider
labeled
.disabled=${stateObj.state === UNAVAILABLE}
.dir=${computeRTLDirection(this.hass)}
.step=${Number(stateObj.attributes.step)}
.min=${Number(stateObj.attributes.min)}
.max=${Number(stateObj.attributes.max)}

View File

@ -260,11 +260,6 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow {
--mdc-icon-size: 40px;
}
:host([rtl]) .flex {
margin-left: 0;
margin-right: 16px;
}
.pointer {
cursor: pointer;
}

View File

@ -73,6 +73,8 @@ export class LovelacePanel extends LitElement {
private _unsubUpdates?: Promise<UnsubscribeFunc>;
private _loading = false;
public connectedCallback(): void {
super.connectedCallback();
if (
@ -162,7 +164,7 @@ export class LovelacePanel extends LitElement {
protected willUpdate(changedProps: PropertyValues) {
super.willUpdate(changedProps);
if (!this.lovelace && this._panelState !== "error") {
if (!this.lovelace && this._panelState !== "error" && !this._loading) {
this._fetchConfig(false);
}
}
@ -233,6 +235,8 @@ export class LovelacePanel extends LitElement {
}
private async _fetchConfig(forceDiskRefresh: boolean) {
this._loading = true;
let conf: LovelaceConfig;
let rawConf: LovelaceRawConfig | undefined;
let confMode: Lovelace["mode"] = this.panel!.config.mode;
@ -301,6 +305,7 @@ export class LovelacePanel extends LitElement {
rawConf = DEFAULT_CONFIG;
confMode = "generated";
} finally {
this._loading = false;
// Ignore updates for another 2 seconds.
if (this.lovelace && this.lovelace.mode === "yaml") {
setTimeout(() => {

View File

@ -8,9 +8,7 @@ import {
TemplateResult,
} from "lit";
import { property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeRTL } from "../../../common/util/compute_rtl";
import { nextRender } from "../../../common/util/render-status";
import "../../../components/entity/ha-state-label-badge";
import "../../../components/ha-svg-icon";
@ -97,9 +95,6 @@ export class MasonryView extends LitElement implements LovelaceViewElement {
)}
extended
@click=${this._addCard}
class=${classMap({
rtl: computeRTL(this.hass!),
})}
>
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
</ha-fab>
@ -338,14 +333,11 @@ export class MasonryView extends LitElement implements LovelaceViewElement {
position: fixed;
right: calc(16px + env(safe-area-inset-right));
bottom: calc(16px + env(safe-area-inset-bottom));
inset-inline-end: calc(16px + env(safe-area-inset-right));
inset-inline-start: initial;
z-index: 1;
}
ha-fab.rtl {
right: auto;
left: calc(16px + env(safe-area-inset-left));
}
@media (max-width: 500px) {
.column > * {
margin-left: 0;

View File

@ -137,12 +137,9 @@ export class PanelView extends LitElement implements LovelaceViewElement {
right: calc(16px + env(safe-area-inset-right));
bottom: calc(16px + env(safe-area-inset-bottom));
z-index: 1;
}
ha-fab.rtl {
float: left;
right: auto;
left: calc(16px + env(safe-area-inset-left));
float: var(--float-end);
inset-inline-end: calc(16px + env(safe-area-inset-right));
inset-inline-start: initial;
}
`;
}

View File

@ -8,9 +8,7 @@ import {
html,
} from "lit";
import { property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeRTL } from "../../../common/util/compute_rtl";
import type { LovelaceViewElement } from "../../../data/lovelace";
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
import type { HomeAssistant } from "../../../types";
@ -100,9 +98,6 @@ export class SideBarView extends LitElement implements LovelaceViewElement {
)}
extended
@click=${this._addCard}
class=${classMap({
rtl: computeRTL(this.hass!),
})}
>
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
</ha-fab>
@ -241,13 +236,10 @@ export class SideBarView extends LitElement implements LovelaceViewElement {
position: fixed;
right: calc(16px + env(safe-area-inset-right));
bottom: calc(16px + env(safe-area-inset-bottom));
inset-inline-end: calc(16px + env(safe-area-inset-right));
inset-inline-start: initial;
z-index: 1;
}
ha-fab.rtl {
right: auto;
left: calc(16px + env(safe-area-inset-left));
}
`;
}
}

View File

@ -430,6 +430,8 @@ class PanelTodo extends LitElement {
position: fixed;
right: 16px;
bottom: 16px;
inset-inline-end: 16px;
inset-inline-start: initial;
}
`,
];

View File

@ -62,22 +62,16 @@ export const sidebarEditStyle = css`
position: absolute;
top: 0;
right: 4px;
inset-inline-end: 4px;
inset-inline-start: initial;
--mdc-icon-button-size: 40px;
}
:host([rtl]) .show-panel {
right: initial;
left: 4px;
}
.hide-panel {
top: 4px;
right: 8px;
}
:host([rtl]) .hide-panel {
right: initial;
left: 8px;
inset-inline-end: 8px;
inset-inline-start: initial;
}
:host([expanded]) .hide-panel {

View File

@ -2,7 +2,6 @@ import type { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { stateActive } from "../common/entity/state_active";
import { computeRTL } from "../common/util/compute_rtl";
import "../components/entity/ha-entity-toggle";
import "../components/entity/state-info";
import { haStyle } from "../resources/styles";
@ -16,9 +15,6 @@ class StateCardAlert extends LitElement {
@property({ type: Boolean }) public inDialog = false;
// property used only in CSS
@property({ type: Boolean, reflect: true }) public rtl = false;
protected render(): TemplateResult {
return html`
<div class="horizontal justified layout">
@ -40,18 +36,6 @@ class StateCardAlert extends LitElement {
`;
}
protected updated(changedProps) {
super.updated(changedProps);
if (!changedProps.has("hass")) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.language !== this.hass.language) {
this.rtl = computeRTL(this.hass);
}
}
static get styles(): CSSResultGroup {
return [
haStyle,
@ -69,7 +53,6 @@ class StateCardAlert extends LitElement {
overflow-wrap: break-word;
display: flex;
align-items: center;
direction: ltr;
}
ha-entity-toggle {
margin: -4px -16px -4px 0;

View File

@ -3,7 +3,6 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { computeDomain } from "../common/entity/compute_domain";
import { computeRTL } from "../common/util/compute_rtl";
import "../components/entity/state-info";
import { isUnavailableState } from "../data/entity";
import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../data/sensor";
@ -53,18 +52,6 @@ class StateCardDisplay extends LitElement {
`;
}
protected updated(changedProps) {
super.updated(changedProps);
if (!changedProps.has("hass")) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.language !== this.hass.language) {
this.rtl = computeRTL(this.hass);
}
}
static get styles(): CSSResultGroup {
return [
haStyle,
@ -83,7 +70,6 @@ class StateCardDisplay extends LitElement {
word-break: break-word;
display: flex;
align-items: center;
direction: ltr;
justify-content: flex-end;
}
.state.has-unit_of_measurement {

View File

@ -1,7 +1,6 @@
import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { computeRTLDirection } from "../common/util/compute_rtl";
import { debounce } from "../common/util/debounce";
import "../components/entity/state-info";
import "../components/ha-slider";
@ -58,7 +57,6 @@ class StateCardInputNumber extends LitElement {
<div class="flex">
<ha-slider
labeled
.dir=${computeRTLDirection(this.hass)}
.disabled=${isUnavailableState(this.stateObj.state)}
.step=${Number(this.stateObj.attributes.step)}
.min=${Number(this.stateObj.attributes.min)}

View File

@ -7,7 +7,6 @@ import "../components/ha-textfield";
import { HomeAssistant } from "../types";
import { haStyle } from "../resources/styles";
import { loadPolyfillIfNeeded } from "../resources/resize-observer.polyfill";
import { computeRTLDirection } from "../common/util/compute_rtl";
import { isUnavailableState } from "../data/entity";
import { debounce } from "../common/util/debounce";
@ -61,7 +60,6 @@ class StateCardNumber extends LitElement {
<div class="flex">
<ha-slider
labeled
.dir=${computeRTLDirection(this.hass)}
.disabled=${isUnavailableState(this.stateObj.state)}
.step=${Number(this.stateObj.attributes.step)}
.min=${Number(this.stateObj.attributes.min)}

View File

@ -4625,7 +4625,7 @@
"device_actions": {
"reinterview_device": "Re-interview device",
"ping_device": "Ping device",
"open_commissioning_window": "Enable commisisioning mode",
"open_commissioning_window": "Share device",
"manage_fabrics": "Manage fabrics",
"view_thread_network": "View Thread network"
},
@ -4657,11 +4657,12 @@
"ping_complete": "Ping device complete."
},
"open_commissioning_window": {
"title": "Enable commissioning mode",
"title": "Share device",
"introduction": "Enable commissioning mode on the device to pair it to another Matter controller.",
"start_commissioning": "Enable commissioning mode",
"start_commissioning": "Share device",
"in_progress": "We're communicating with the device. This may take some time.",
"failed": "The command failed. Additional information may be available in the logs.",
"success": "Your device can now be added to another Matter controller, scan the QR code below or enter the sharing code in the app of the controller you want to add your device to.",
"sharing_code": "Sharing code"
}
},
@ -6505,7 +6506,9 @@
"start_search": "Start by selecting an area, device or entity above",
"add_all": "Add all entities",
"remove_all": "Remove all selections",
"download_data": "Download data"
"download_data": "Download data",
"download_data_error": "Unable to download data",
"error_no_data": "You need to select data first."
}
},
"tips": {

134
yarn.lock
View File

@ -4515,15 +4515,15 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:6.19.1":
version: 6.19.1
resolution: "@typescript-eslint/eslint-plugin@npm:6.19.1"
"@typescript-eslint/eslint-plugin@npm:6.20.0":
version: 6.20.0
resolution: "@typescript-eslint/eslint-plugin@npm:6.20.0"
dependencies:
"@eslint-community/regexpp": "npm:^4.5.1"
"@typescript-eslint/scope-manager": "npm:6.19.1"
"@typescript-eslint/type-utils": "npm:6.19.1"
"@typescript-eslint/utils": "npm:6.19.1"
"@typescript-eslint/visitor-keys": "npm:6.19.1"
"@typescript-eslint/scope-manager": "npm:6.20.0"
"@typescript-eslint/type-utils": "npm:6.20.0"
"@typescript-eslint/utils": "npm:6.20.0"
"@typescript-eslint/visitor-keys": "npm:6.20.0"
debug: "npm:^4.3.4"
graphemer: "npm:^1.4.0"
ignore: "npm:^5.2.4"
@ -4536,44 +4536,44 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
checksum: e88a35527b066a42d0253d153183a360faedc1cd39867c541ce7cb1f7b22f8446bb913b998fcdeba269d5d4217888af42e6d64da5c0592b1f49ed5648d2e3e84
checksum: dee6a2392c831e6ae69611ecc4de06e66a7b16f6bf6d8e3bfd25091eb14d88c9d0bb9c9cd634efcfa318902341f7a459cf48f713d55cb1d610145ca1f52af4d3
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:6.19.1":
version: 6.19.1
resolution: "@typescript-eslint/parser@npm:6.19.1"
"@typescript-eslint/parser@npm:6.20.0":
version: 6.20.0
resolution: "@typescript-eslint/parser@npm:6.20.0"
dependencies:
"@typescript-eslint/scope-manager": "npm:6.19.1"
"@typescript-eslint/types": "npm:6.19.1"
"@typescript-eslint/typescript-estree": "npm:6.19.1"
"@typescript-eslint/visitor-keys": "npm:6.19.1"
"@typescript-eslint/scope-manager": "npm:6.20.0"
"@typescript-eslint/types": "npm:6.20.0"
"@typescript-eslint/typescript-estree": "npm:6.20.0"
"@typescript-eslint/visitor-keys": "npm:6.20.0"
debug: "npm:^4.3.4"
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
peerDependenciesMeta:
typescript:
optional: true
checksum: 63ff00a56586879a62e40b27b55c94501173fcf2fb5a620d01e7505851b4bb20feb1e7fbad36010af97aefc0a722267d9ce3aa004abab22cb7eb23eebb0102ce
checksum: 691062d47cae7977604ede848ffff3689162428a53577f298989f585954aa3a3450e7fd5c2b363d024cd5f16022c163cecf0f1f1d138234bbd78048050b4b8bf
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:6.19.1":
version: 6.19.1
resolution: "@typescript-eslint/scope-manager@npm:6.19.1"
"@typescript-eslint/scope-manager@npm:6.20.0":
version: 6.20.0
resolution: "@typescript-eslint/scope-manager@npm:6.20.0"
dependencies:
"@typescript-eslint/types": "npm:6.19.1"
"@typescript-eslint/visitor-keys": "npm:6.19.1"
checksum: 2a17f68d3c41582bfac7ecd192e2c2539cf4d2c9728a7018d842da7a8a23986b8a1f8cfcb59862c909b483140a2d164a4ba44451905e0a141378e5dd0df056cc
"@typescript-eslint/types": "npm:6.20.0"
"@typescript-eslint/visitor-keys": "npm:6.20.0"
checksum: 2c1a644f2931454b34875f2e6dffad52a1fc7b6ac508d7d1ad3cd9da028a7dff9c6191feeea2c9ca691deba199ac9e83cbd0036914be4cd45b6954437f03c09a
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:6.19.1":
version: 6.19.1
resolution: "@typescript-eslint/type-utils@npm:6.19.1"
"@typescript-eslint/type-utils@npm:6.20.0":
version: 6.20.0
resolution: "@typescript-eslint/type-utils@npm:6.20.0"
dependencies:
"@typescript-eslint/typescript-estree": "npm:6.19.1"
"@typescript-eslint/utils": "npm:6.19.1"
"@typescript-eslint/typescript-estree": "npm:6.20.0"
"@typescript-eslint/utils": "npm:6.20.0"
debug: "npm:^4.3.4"
ts-api-utils: "npm:^1.0.1"
peerDependencies:
@ -4581,23 +4581,23 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
checksum: 5150b897d8b3778c549c6b964b031981da1039dfa0fb89a0eb92702735ca55793d2f840af14b340eccbca81669ba3dd02d7f09fb420fb66b18ec9f1f211b3243
checksum: bc2f2793cfec3463164b5f5ded31b4e169e21c3a1990c1ce4effe70a359c486d92fbbc4cd92758bbf1c30a468ad0839e0fa890bd452c707d0c294cb3a7b14021
languageName: node
linkType: hard
"@typescript-eslint/types@npm:6.19.1":
version: 6.19.1
resolution: "@typescript-eslint/types@npm:6.19.1"
checksum: 93f3ded80b81a1b8686866b93e36ddf9bac04604d09e88d7ed1ec25b6b2f49ff64747d8d194ba1f3215e231fd0790b88fb5ecadcc6ed53ff584f8c0b87423216
"@typescript-eslint/types@npm:6.20.0":
version: 6.20.0
resolution: "@typescript-eslint/types@npm:6.20.0"
checksum: 74ed1761e27c3c1a29fd260fe51096f42cfb1472b20390d6df6ec41de0420208f379e809de416e81cd7c00fdc3d5550b2391872be56bf4a1b0c595f71db0b1ea
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:6.19.1":
version: 6.19.1
resolution: "@typescript-eslint/typescript-estree@npm:6.19.1"
"@typescript-eslint/typescript-estree@npm:6.20.0":
version: 6.20.0
resolution: "@typescript-eslint/typescript-estree@npm:6.20.0"
dependencies:
"@typescript-eslint/types": "npm:6.19.1"
"@typescript-eslint/visitor-keys": "npm:6.19.1"
"@typescript-eslint/types": "npm:6.20.0"
"@typescript-eslint/visitor-keys": "npm:6.20.0"
debug: "npm:^4.3.4"
globby: "npm:^11.1.0"
is-glob: "npm:^4.0.3"
@ -4607,34 +4607,34 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
checksum: 3ce91dd477ccb2cc3cf5d07ac8d23792988f4fad78bfd39783292846f32daea5081d3790ba9cc795d9de89ea2e1d55dc9c3d2aeaa8597093b0f6ac3a206195e9
checksum: 55b280c6e71c79cb009ac80189a7f0e1aa9011bc7206c810bbb52d9703a894aa2817dfd44d947edf64d62f3aa0962e01f3423fcb21d2f39964a4840287d9e196
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:6.19.1":
version: 6.19.1
resolution: "@typescript-eslint/utils@npm:6.19.1"
"@typescript-eslint/utils@npm:6.20.0":
version: 6.20.0
resolution: "@typescript-eslint/utils@npm:6.20.0"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.4.0"
"@types/json-schema": "npm:^7.0.12"
"@types/semver": "npm:^7.5.0"
"@typescript-eslint/scope-manager": "npm:6.19.1"
"@typescript-eslint/types": "npm:6.19.1"
"@typescript-eslint/typescript-estree": "npm:6.19.1"
"@typescript-eslint/scope-manager": "npm:6.20.0"
"@typescript-eslint/types": "npm:6.20.0"
"@typescript-eslint/typescript-estree": "npm:6.20.0"
semver: "npm:^7.5.4"
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
checksum: f8931df675defa84af373c81bbb13cc34c2fcf0803c687a38b982e85335dbf2fb8415667fbabaa043df0326ba3e98ed974104bbd21f09ec538304fc3adeed0c3
checksum: 6d4604be6123e0073dd5e7dd357c95b370c678572d2e982478d0d6937d4d65f0cad0ac207b8b724f3bce239e64ba1ddd6bece11e1592734d8bf691177e6971e6
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:6.19.1":
version: 6.19.1
resolution: "@typescript-eslint/visitor-keys@npm:6.19.1"
"@typescript-eslint/visitor-keys@npm:6.20.0":
version: 6.20.0
resolution: "@typescript-eslint/visitor-keys@npm:6.20.0"
dependencies:
"@typescript-eslint/types": "npm:6.19.1"
"@typescript-eslint/types": "npm:6.20.0"
eslint-visitor-keys: "npm:^3.4.1"
checksum: b41f3247520e1e4d3e43876843b03f0d887e544d4ac8a9e1f4b25d08568da36fedde883fa226488a595f688198859cd0290d0f1351c2ca6cbc30cca2c90adf21
checksum: df066c73f3880ad78880c442f307e58f026e6047d9caab9d7c356d13276f4fe466fab3e8d19cdb1e6749e87639cb7c4babcfe118f554fcd2d3929ce9f4983216
languageName: node
linkType: hard
@ -7909,9 +7909,9 @@ __metadata:
languageName: node
linkType: hard
"eslint-plugin-lit-a11y@npm:4.1.1":
version: 4.1.1
resolution: "eslint-plugin-lit-a11y@npm:4.1.1"
"eslint-plugin-lit-a11y@npm:4.1.2":
version: 4.1.2
resolution: "eslint-plugin-lit-a11y@npm:4.1.2"
dependencies:
aria-query: "npm:^5.1.3"
axe-core: "npm:^4.3.3"
@ -7926,7 +7926,7 @@ __metadata:
requireindex: "npm:~1.2.0"
peerDependencies:
eslint: ">= 5"
checksum: f8d20e4e18ea9cf17c95546f878213d57f3eb9fd71a01ebae1b5d1689c0da7c35f0fe39137994717e600c3d12cfaa754ab3d4cdd854a6cf4d0a907ae898bc9a5
checksum: 2d70f0b9fa6afc7f259877acd7e69c14f0104a69a019efb594d5de603e12b982e4a96fec5b169005fab244655951f85bff77f469d9aeadb885974f963a7d9996
languageName: node
linkType: hard
@ -9437,10 +9437,10 @@ __metadata:
languageName: node
linkType: hard
"hls.js@npm:1.5.2":
version: 1.5.2
resolution: "hls.js@npm:1.5.2"
checksum: bf92e8f005eb4e906bc19a0baf6428df4cbfd344b517de70496b953d77f1231324d26b4dc88c375d3cd481311601c1d1d5aea34ec4e0fc96e9df22b9b0e28a84
"hls.js@npm:1.5.3":
version: 1.5.3
resolution: "hls.js@npm:1.5.3"
checksum: c5b7cb6fddd6ad425a82e1b8b4f818552e6e242254b35c7d7c7b9d3beb4dd40cc85d8bbc32a5d1b57031305d7d9f4973f9d0f80b70351bb8597393f4629072fc
languageName: node
linkType: hard
@ -9546,8 +9546,8 @@ __metadata:
"@types/tar": "npm:6.1.11"
"@types/ua-parser-js": "npm:0.7.39"
"@types/webspeechapi": "npm:0.0.29"
"@typescript-eslint/eslint-plugin": "npm:6.19.1"
"@typescript-eslint/parser": "npm:6.19.1"
"@typescript-eslint/eslint-plugin": "npm:6.20.0"
"@typescript-eslint/parser": "npm:6.20.0"
"@vaadin/combo-box": "npm:24.3.4"
"@vaadin/vaadin-themable-mixin": "npm:24.3.4"
"@vibrant/color": "npm:3.2.1-alpha.1"
@ -9580,7 +9580,7 @@ __metadata:
eslint-plugin-disable: "npm:2.0.3"
eslint-plugin-import: "npm:2.29.1"
eslint-plugin-lit: "npm:1.11.0"
eslint-plugin-lit-a11y: "npm:4.1.1"
eslint-plugin-lit-a11y: "npm:4.1.2"
eslint-plugin-unused-imports: "npm:3.0.0"
eslint-plugin-wc: "npm:2.0.4"
fancy-log: "npm:2.0.0"
@ -9594,10 +9594,10 @@ __metadata:
gulp-merge-json: "npm:2.1.2"
gulp-rename: "npm:2.0.0"
gulp-zopfli-green: "npm:6.0.1"
hls.js: "npm:1.5.2"
hls.js: "npm:1.5.3"
home-assistant-js-websocket: "npm:9.1.0"
html-minifier-terser: "npm:7.2.0"
husky: "npm:9.0.6"
husky: "npm:9.0.7"
idb-keyval: "npm:6.2.1"
instant-mocha: "npm:1.5.2"
intl-messageformat: "npm:10.5.11"
@ -9879,12 +9879,12 @@ __metadata:
languageName: node
linkType: hard
"husky@npm:9.0.6":
version: 9.0.6
resolution: "husky@npm:9.0.6"
"husky@npm:9.0.7":
version: 9.0.7
resolution: "husky@npm:9.0.7"
bin:
husky: bin.js
checksum: e198c90a59d460cf860c33e0a4c3927ecfb645d4fd4c2de3fbcd5fb56b858a923af452508d549f6ed020bb48de08290912cd77c006dd2a83e551c24c17340d5b
checksum: c5673acb9f224b6d2b36ee4892c6b99dbe9a077b041aee6013efeacb71d2d2cbe6ab7fac061f68fa5653c7dbe93aacfeed69eea01f755688e4c36c4044dcc5c0
languageName: node
linkType: hard