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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,6 +9,7 @@ import {
localizeWeekdays, localizeWeekdays,
localizeMonths, localizeMonths,
} from "../common/datetime/localize_date"; } 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 // Set the current date to the left picker instead of the right picker because the right is hidden
const CustomDateRangePicker = Vue.extend({ const CustomDateRangePicker = Vue.extend({
@ -157,7 +158,7 @@ class DateRangePickerElement extends WrappedElement {
min-width: initial !important; min-width: initial !important;
max-height: var(--date-range-picker-max-height); max-height: var(--date-range-picker-max-height);
overflow-y: auto; overflow-y: auto;
} }
.daterangepicker:before { .daterangepicker:before {
display: none; display: none;
} }
@ -267,15 +268,37 @@ class DateRangePickerElement extends WrappedElement {
.calendar-table { .calendar-table {
padding: 0 !important; padding: 0 !important;
} }
.daterangepicker.ltr { .calendar-time {
direction: ltr; direction: ltr;
text-align: left; }
.daterangepicker.ltr {
direction: var(--direction);
text-align: var(--float-start);
} }
.vue-daterange-picker{ .vue-daterange-picker{
min-width: unset !important; min-width: unset !important;
display: block !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!; const shadowRoot = this.shadowRoot!;
shadowRoot.appendChild(style); shadowRoot.appendChild(style);
// Stop click events from reaching the document, otherwise it will close the picker immediately. // 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 { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { computeStateName } from "../../common/entity/compute_state_name"; import { computeStateName } from "../../common/entity/compute_state_name";
import { computeRTL } from "../../common/util/compute_rtl";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import "../ha-relative-time"; import "../ha-relative-time";
import "./state-badge"; import "./state-badge";
@ -16,9 +15,6 @@ class StateInfo extends LitElement {
@property({ type: Boolean }) public inDialog = false; @property({ type: Boolean }) public inDialog = false;
// property used only in CSS
@property({ type: Boolean, reflect: true }) public rtl = false;
@property() public color?: string; @property() public color?: string;
protected render() { protected render() {
@ -79,18 +75,6 @@ class StateInfo extends LitElement {
</div>`; </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 { static get styles(): CSSResultGroup {
return css` return css`
:host { :host {
@ -106,17 +90,14 @@ class StateInfo extends LitElement {
.info { .info {
margin-left: 8px; margin-left: 8px;
margin-inline-start: 8px;
margin-inline-end: initial;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
height: 100%; height: 100%;
min-width: 0; min-width: 0;
} text-align: var(--float-start);
:host([rtl]) .info {
margin-right: 8px;
margin-left: 0;
text-align: right;
} }
.name { .name {

View File

@ -69,6 +69,7 @@ export class HaButtonToggleGroup extends LitElement {
display: flex; display: flex;
--mdc-icon-button-size: var(--button-toggle-size, 36px); --mdc-icon-button-size: var(--button-toggle-size, 36px);
--mdc-icon-size: var(--button-toggle-icon-size, 20px); --mdc-icon-size: var(--button-toggle-icon-size, 20px);
direction: ltr;
} }
mwc-button { mwc-button {
--mdc-shape-small: 0; --mdc-shape-small: 0;
@ -119,19 +120,6 @@ export class HaButtonToggleGroup extends LitElement {
--mdc-shape-small: 4px; --mdc-shape-small: 4px;
border-right-width: 1px; 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 { formatDate } from "../common/datetime/format_date";
import { formatDateTime } from "../common/datetime/format_date_time"; import { formatDateTime } from "../common/datetime/format_date_time";
import { useAmPm } from "../common/datetime/use_am_pm"; import { useAmPm } from "../common/datetime/use_am_pm";
import { computeRTLDirection } from "../common/util/compute_rtl";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
import "./date-range-picker"; import "./date-range-picker";
import "./ha-icon-button"; import "./ha-icon-button";
@ -65,8 +64,6 @@ export class HaDateRangePicker extends LitElement {
@state() private _hour24format = false; @state() private _hour24format = false;
@state() private _rtlDirection = "ltr";
@property({ type: Boolean }) public extendedPresets = false; @property({ type: Boolean }) public extendedPresets = false;
@property() public openingDirection?: "right" | "left" | "center" | "inline"; @property() public openingDirection?: "right" | "left" | "center" | "inline";
@ -236,7 +233,6 @@ export class HaDateRangePicker extends LitElement {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined; const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass.locale) { if (!oldHass || oldHass.locale !== this.hass.locale) {
this._hour24format = !useAmPm(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>`} ></ha-icon-button>`}
</div> </div>
${this.ranges !== false && (this.ranges || this._ranges) ${this.ranges !== false && (this.ranges || this._ranges)
? html`<div ? html`<div slot="ranges" class="date-range-ranges">
slot="ranges"
class="date-range-ranges"
.dir=${this._rtlDirection}
>
<mwc-list @action=${this._setDateRange} activatable> <mwc-list @action=${this._setDateRange} activatable>
${Object.keys(this.ranges || this._ranges!).map( ${Object.keys(this.ranges || this._ranges!).map(
(name) => html`<mwc-list-item>${name}</mwc-list-item>` (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 { css, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { mainWindow } from "../common/dom/get_main_window";
const blockingElements = (document as any).$blockingElements; const blockingElements = (document as any).$blockingElements;
@ -12,6 +13,8 @@ export class HaDrawer extends DrawerBase {
private _mc?: HammerManager; private _mc?: HammerManager;
private _rtlStyle?: HTMLElement;
protected createAdapter() { protected createAdapter() {
return { return {
...super.createAdapter(), ...super.createAdapter(),
@ -31,8 +34,26 @@ export class HaDrawer extends DrawerBase {
protected updated(changedProps: PropertyValues) { protected updated(changedProps: PropertyValues) {
super.updated(changedProps); super.updated(changedProps);
if (changedProps.has("direction")) { 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") { if (changedProps.has("open") && this.open && this.type === "modal") {
this._setupSwipe(); this._setupSwipe();
} else if (this._mc) { } else if (this._mc) {
@ -66,6 +87,8 @@ export class HaDrawer extends DrawerBase {
position: fixed; position: fixed;
top: 0; top: 0;
border-color: var(--divider-color, rgba(0, 0, 0, 0.12)); 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 { .mdc-drawer.mdc-drawer--modal.mdc-drawer--open {
z-index: 200; z-index: 200;

View File

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

View File

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

View File

@ -39,6 +39,12 @@ export class HaSortable extends LitElement {
@property({ type: String, attribute: "group" }) @property({ type: String, attribute: "group" })
public group?: string; 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>) { protected updated(changedProperties: PropertyValues<this>) {
if (changedProperties.has("disabled")) { if (changedProperties.has("disabled")) {
if (this.disabled) { if (this.disabled) {
@ -81,7 +87,7 @@ export class HaSortable extends LitElement {
} }
.sortable-ghost { .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); background: rgba(var(--rgb-primary-color), 0.25);
border-radius: 4px; border-radius: 4px;
opacity: 0.4; opacity: 0.4;
@ -108,7 +114,7 @@ export class HaSortable extends LitElement {
const options: SortableInstance.Options = { const options: SortableInstance.Options = {
animation: 150, animation: 150,
swapThreshold: 0.75, swapThreshold: 1,
onChoose: this._handleChoose, onChoose: this._handleChoose,
onEnd: this._handleEnd, onEnd: this._handleEnd,
}; };
@ -116,6 +122,13 @@ export class HaSortable extends LitElement {
if (this.draggableSelector) { if (this.draggableSelector) {
options.draggable = 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) { if (this.handleSelector) {
options.handle = 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 { styles as textareaStyles } from "@material/mwc-textarea/mwc-textarea.css";
import { css, PropertyValues } from "lit"; import { css, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { mainWindow } from "../common/dom/get_main_window";
@customElement("ha-textarea") @customElement("ha-textarea")
export class HaTextArea extends TextAreaBase { export class HaTextArea extends TextAreaBase {
@property({ type: Boolean, reflect: true }) autogrow = false; @property({ type: Boolean, reflect: true }) autogrow = false;
firstUpdated() {
super.firstUpdated();
this.setAttribute("dir", mainWindow.document.dir);
}
updated(changedProperties: PropertyValues) { updated(changedProperties: PropertyValues) {
super.updated(changedProperties); super.updated(changedProperties);
if (this.autogrow && changedProperties.has("value")) { if (this.autogrow && changedProperties.has("value")) {
@ -54,9 +47,10 @@ export class HaTextArea extends TextAreaBase {
margin-top: 16px; margin-top: 16px;
margin-bottom: 16px; margin-bottom: 16px;
} }
:host([dir="rtl"]) .mdc-floating-label { .mdc-floating-label {
right: 16px; inset-inline-start: 16px !important;
left: initial; inset-inline-end: initial !important;
transform-origin: var(--float-start) top;
} }
`, `,
]; ];

View File

@ -8,12 +8,18 @@ import type {
Marker, Marker,
Polyline, Polyline,
} from "leaflet"; } from "leaflet";
import { isToday } from "date-fns";
import { css, CSSResultGroup, PropertyValues, ReactiveElement } from "lit"; import { css, CSSResultGroup, PropertyValues, ReactiveElement } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { import {
LeafletModuleType, LeafletModuleType,
setupLeafletMap, setupLeafletMap,
} from "../../common/dom/setup-leaflet-map"; } 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 { computeStateDomain } from "../../common/entity/compute_state_domain";
import { computeStateName } from "../../common/entity/compute_state_name"; import { computeStateName } from "../../common/entity/compute_state_name";
import { loadPolyfillIfNeeded } from "../../resources/resize-observer.polyfill"; import { loadPolyfillIfNeeded } from "../../resources/resize-observer.polyfill";
@ -27,12 +33,14 @@ const getEntityId = (entity: string | HaMapEntity): string =>
export interface HaMapPathPoint { export interface HaMapPathPoint {
point: LatLngTuple; point: LatLngTuple;
tooltip: string; timestamp: Date;
} }
export interface HaMapPaths { export interface HaMapPaths {
points: HaMapPathPoint[]; points: HaMapPathPoint[];
color?: string; color?: string;
name?: string;
gradualOpacity?: number; gradualOpacity?: number;
fullDatetime?: boolean;
} }
export interface HaMapEntity { 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 { private _drawPaths(): void {
const hass = this.hass; const hass = this.hass;
const map = this.leafletMap; const map = this.leafletMap;
@ -289,7 +321,10 @@ export class HaMap extends ReactiveElement {
fillOpacity: opacity, fillOpacity: opacity,
interactive: true, 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 // DRAW line between this and next point
@ -319,7 +354,10 @@ export class HaMap extends ReactiveElement {
fillOpacity: opacity, fillOpacity: opacity,
interactive: true, 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)); this._mapPaths.forEach((marker) => map.addLayer(marker));
@ -556,6 +594,7 @@ export class HaMap extends ReactiveElement {
color: white !important; color: white !important;
border-radius: 4px; border-radius: 4px;
box-shadow: none !important; 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 { styleMap } from "lit/directives/style-map";
import { until } from "lit/directives/until"; import { until } from "lit/directives/until";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { computeRTLDirection } from "../../common/util/compute_rtl";
import { debounce } from "../../common/util/debounce"; import { debounce } from "../../common/util/debounce";
import { isUnavailableState } from "../../data/entity"; import { isUnavailableState } from "../../data/entity";
import type { MediaPlayerItem } from "../../data/media-player"; import type { MediaPlayerItem } from "../../data/media-player";
@ -539,7 +538,6 @@ export class HaMediaPlayerBrowse extends LitElement {
.graphic=${mediaClass.show_list_images .graphic=${mediaClass.show_list_images
? "medium" ? "medium"
: "avatar"} : "avatar"}
dir=${computeRTLDirection(this.hass)}
> >
<span class="title"> <span class="title">
${this.hass.localize( ${this.hass.localize(
@ -637,7 +635,6 @@ export class HaMediaPlayerBrowse extends LitElement {
@click=${this._childClicked} @click=${this._childClicked}
.item=${child} .item=${child}
.graphic=${mediaClass.show_list_images ? "medium" : "avatar"} .graphic=${mediaClass.show_list_images ? "medium" : "avatar"}
dir=${computeRTLDirection(this.hass)}
> >
${backgroundImage === "none" && !child.can_play ${backgroundImage === "none" && !child.can_play
? html`<ha-svg-icon ? html`<ha-svg-icon
@ -1198,10 +1195,8 @@ export class HaMediaPlayerBrowse extends LitElement {
mwc-list-item .title { mwc-list-item .title {
margin-left: 16px; margin-left: 16px;
} margin-inline-start: 16px;
mwc-list-item[dir="rtl"] .title { margin-inline-end: initial;
margin-right: 16px;
margin-left: 0;
} }
/* ============= Narrow ============= */ /* ============= Narrow ============= */
@ -1332,6 +1327,10 @@ export class HaMediaPlayerBrowse extends LitElement {
lit-virtualizer.not_shown { lit-virtualizer.not_shown {
height: calc(100% - 36px); height: calc(100% - 36px);
} }
ha-browse-media-tts {
direction: var(--direction);
}
`, `,
]; ];
} }

View File

@ -8,30 +8,41 @@ import {
EntityRegistryDisplayEntry, EntityRegistryDisplayEntry,
EntityRegistryEntry, EntityRegistryEntry,
} from "./entity_registry"; } 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: {},
entity_component: undefined, entity_component: {},
services: {}, services: { domains: {} },
}; };
interface IconResources { interface IconResources<
resources: Record<string, string | Record<string, string>>; T extends ComponentIcons | PlatformIcons | ServiceIcons,
> {
resources: Record<string, T>;
} }
interface PlatformIcons { interface PlatformIcons {
[domain: string]: { [translation_key: string]: {
[translation_key: string]: { state: Record<string, string>;
state: Record<string, string>; state_attributes: Record<
state_attributes: Record< string,
string, {
{ state: Record<string, string>;
state: Record<string, string>; default: string;
default: string; }
} >;
>; default: string;
default: string;
};
}; };
} }
@ -55,12 +66,18 @@ interface ServiceIcons {
export type IconCategory = "entity" | "entity_component" | "services"; 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, hass: HomeAssistant,
category: IconCategory, category: T,
integration?: string integration?: string
): Promise<IconResources> => ) =>
hass.callWS<{ resources: Record<string, string> }>({ hass.callWS<IconResources<CategoryType[T]>>({
type: "frontend/get_icons", type: "frontend/get_icons",
category, category,
integration, integration,
@ -70,14 +87,17 @@ export const getPlatformIcons = async (
hass: HomeAssistant, hass: HomeAssistant,
integration: string, integration: string,
force = false force = false
): Promise<PlatformIcons> => { ): Promise<PlatformIcons | undefined> => {
if (!force && integration in resources.entity) { if (!force && integration in resources.entity) {
return resources.entity[integration]; return resources.entity[integration];
} }
const result = getHassIcons(hass, "entity", integration); if (!isComponentLoaded(hass, integration)) {
resources.entity[integration] = result.then( return undefined;
}
const result = getHassIcons(hass, "entity", integration).then(
(res) => res?.resources[integration] (res) => res?.resources[integration]
); );
resources.entity[integration] = result;
return resources.entity[integration]; return resources.entity[integration];
}; };
@ -85,45 +105,59 @@ export const getComponentIcons = async (
hass: HomeAssistant, hass: HomeAssistant,
domain: string, domain: string,
force = false force = false
): Promise<ComponentIcons> => { ): Promise<ComponentIcons | undefined> => {
if (!force && resources.entity_component) { if (
return resources.entity_component.then((res) => res[domain]); !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( if (!isComponentLoaded(hass, domain)) {
(result) => result.resources return undefined;
); }
return resources.entity_component.then((res) => res[domain]); 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 ( export const getServiceIcons = async (
hass: HomeAssistant, hass: HomeAssistant,
domain?: string, domain?: string,
force = false force = false
): Promise<ServiceIcons> => { ): Promise<ServiceIcons | Record<string, ServiceIcons> | undefined> => {
if (!domain) { if (!domain) {
if (!force && resources.services.all) { if (!force && resources.services.all) {
return resources.services.all; return resources.services.all;
} }
resources.services.all = getHassIcons(hass, "services", domain).then( resources.services.all = getHassIcons(hass, "services", domain).then(
(res) => { (res) => {
resources.services = res.resources; resources.services.domains = res.resources;
return res?.resources; return res?.resources;
} }
); );
return resources.services.all; return resources.services.all;
} }
if (!force && domain && domain in resources.services) { if (!force && domain in resources.services.domains) {
return resources.services[domain]; return resources.services.domains[domain];
} }
if (resources.services.all && !force) { if (resources.services.all && !force) {
await resources.services.all; await resources.services.all;
if (domain in resources.services) { if (domain in resources.services.domains) {
return resources.services[domain]; return resources.services.domains[domain];
} }
} }
if (!isComponentLoaded(hass, domain)) {
return undefined;
}
const result = getHassIcons(hass, "services", domain); const result = getHassIcons(hass, "services", domain);
resources.services[domain] = result.then((res) => res?.resources[domain]); resources.services.domains[domain] = result.then(
return resources.services[domain]; (res) => res?.resources[domain]
);
return resources.services.domains[domain];
}; };
export const entityIcon = async ( export const entityIcon = async (
@ -238,7 +272,7 @@ export const serviceIcon = async (
const serviceName = computeObjectId(service); const serviceName = computeObjectId(service);
const serviceIcons = await getServiceIcons(hass, domain); const serviceIcons = await getServiceIcons(hass, domain);
if (serviceIcons) { if (serviceIcons) {
icon = serviceIcons[serviceName]; icon = serviceIcons[serviceName] as string;
} }
if (!icon) { if (!icon) {
icon = await domainIcon(hass, domain); 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 { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { computeRTLDirection } from "../../common/util/compute_rtl";
import "../../components/ha-dialog"; import "../../components/ha-dialog";
import "../../components/ha-formfield"; import "../../components/ha-formfield";
import "../../components/ha-switch"; import "../../components/ha-switch";
@ -82,7 +81,6 @@ class DialogConfigEntrySystemOptions extends LitElement {
} }
)} )}
</p>`} </p>`}
.dir=${computeRTLDirection(this.hass)}
> >
<ha-switch <ha-switch
.checked=${!this._disableNewEntities} .checked=${!this._disableNewEntities}
@ -109,7 +107,6 @@ class DialogConfigEntrySystemOptions extends LitElement {
} }
)} )}
</p>`} </p>`}
.dir=${computeRTLDirection(this.hass)}
> >
<ha-switch <ha-switch
.checked=${!this._disablePolling} .checked=${!this._disablePolling}

View File

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

View File

@ -1,15 +1,6 @@
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, eventOptions, property } from "lit/decorators"; import { customElement, eventOptions, property } from "lit/decorators";
import { restoreScroll } from "../common/decorators/restore-scroll"; 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-icon-button-arrow-prev";
import "../components/ha-menu-button"; import "../components/ha-menu-button";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
@ -34,17 +25,6 @@ class HassSubpage extends LitElement {
// @ts-ignore // @ts-ignore
@restoreScroll(".content") private _savedScrollPos?: number; @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 { protected render(): TemplateResult {
return html` return html`
<div class="toolbar"> <div class="toolbar">
@ -160,6 +140,9 @@ class HassSubpage extends LitElement {
#fab { #fab {
position: absolute; position: absolute;
right: calc(16px + env(safe-area-inset-right)); 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)); bottom: calc(16px + env(safe-area-inset-bottom));
z-index: 1; z-index: 1;
} }
@ -169,15 +152,8 @@ class HassSubpage extends LitElement {
#fab[is-wide] { #fab[is-wide] {
bottom: 24px; bottom: 24px;
right: 24px; right: 24px;
} inset-inline-end: 24px;
:host([rtl]) #fab { inset-inline-start: initial;
right: auto;
left: calc(16px + env(safe-area-inset-left));
}
:host([rtl][is-wide]) #fab {
bottom: 24px;
left: 24px;
right: auto;
} }
`, `,
]; ];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,7 +18,6 @@ import {
} from "lit"; } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import { computeRTL } from "../../../../../common/util/compute_rtl";
import "../../../../../components/ha-card"; import "../../../../../components/ha-card";
import "../../../../../components/ha-expansion-panel"; import "../../../../../components/ha-expansion-panel";
import "../../../../../components/ha-fab"; import "../../../../../components/ha-fab";
@ -472,7 +471,6 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
"ui.panel.config.zwave_js.common.add_node" "ui.panel.config.zwave_js.common.add_node"
)} )}
extended extended
?rtl=${computeRTL(this.hass)}
@click=${this._addNodeClicked} @click=${this._addNodeClicked}
.disabled=${this._status !== "connected" || .disabled=${this._status !== "connected" ||
(this._network?.controller.inclusion_state !== InclusionState.Idle && (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 { HASSDomEvent, fireEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name"; import { computeStateName } from "../../../common/entity/compute_state_name";
import { navigate } from "../../../common/navigate"; import { navigate } from "../../../common/navigate";
import { computeRTL } from "../../../common/util/compute_rtl";
import { import {
DataTableColumnContainer, DataTableColumnContainer,
RowClickedEvent, RowClickedEvent,
@ -308,7 +307,6 @@ class HaScriptPicker extends LitElement {
"ui.panel.config.script.picker.add_script" "ui.panel.config.script.picker.add_script"
)} )}
extended extended
?rtl=${computeRTL(this.hass)}
@click=${this._createNew} @click=${this._createNew}
> >
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon> <ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import "@material/mwc-button/mwc-button"; import "@material/mwc-button/mwc-button";
import { mdiSlopeUphill } from "@mdi/js"; import { mdiSlopeUphill } from "@mdi/js";
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket"; 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 { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
@ -26,7 +26,6 @@ import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { showStatisticsAdjustSumDialog } from "./show-dialog-statistics-adjust-sum"; import { showStatisticsAdjustSumDialog } from "./show-dialog-statistics-adjust-sum";
import { showFixStatisticsUnitsChangedDialog } from "./show-dialog-statistics-fix-units-changed"; import { showFixStatisticsUnitsChangedDialog } from "./show-dialog-statistics-fix-units-changed";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { documentationUrl } from "../../../util/documentation-url"; import { documentationUrl } from "../../../util/documentation-url";
const FIX_ISSUES_ORDER = { const FIX_ISSUES_ORDER = {
@ -183,7 +182,6 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
id="statistic_id" id="statistic_id"
clickable clickable
@row-click=${this._rowClicked} @row-click=${this._rowClicked}
.dir=${computeRTLDirection(this.hass)}
></ha-data-table> ></ha-data-table>
`; `;
} }
@ -411,45 +409,7 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
}; };
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return [ return haStyle;
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;
}
`,
];
} }
} }

View File

@ -1,5 +1,4 @@
import "@material/mwc-button/mwc-button"; import "@material/mwc-button/mwc-button";
import "@material/mwc-list/mwc-list-item";
import formatISO9075 from "date-fns/formatISO9075"; import formatISO9075 from "date-fns/formatISO9075";
import { import {
css, css,
@ -21,6 +20,7 @@ import "../../../components/ha-selector/ha-selector-datetime";
import "../../../components/ha-selector/ha-selector-number"; import "../../../components/ha-selector/ha-selector-number";
import "../../../components/ha-svg-icon"; import "../../../components/ha-svg-icon";
import "../../../components/ha-icon-next"; import "../../../components/ha-icon-next";
import "../../../components/ha-list-item";
import { import {
adjustStatisticsSum, adjustStatisticsSum,
fetchStatistics, fetchStatistics,
@ -151,7 +151,7 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement {
const stat = data[i]; const stat = data[i];
const growth = Math.round(stat.change! * 100) / 100; const growth = Math.round(stat.change! * 100) / 100;
rows.push(html` rows.push(html`
<mwc-list-item <ha-list-item
twoline twoline
hasMeta hasMeta
.stat=${stat} .stat=${stat}
@ -166,7 +166,7 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement {
)} )}
</span> </span>
<ha-icon-next slot="meta"></ha-icon-next> <ha-icon-next slot="meta"></ha-icon-next>
</mwc-list-item> </ha-list-item>
`); `);
} }
stats = html`${rows}`; stats = html`${rows}`;
@ -413,7 +413,7 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement {
ha-selector-number { ha-selector-number {
margin-bottom: 20px; margin-bottom: 20px;
} }
mwc-list-item { ha-list-item {
margin: 0 -24px; margin: 0 -24px;
--mdc-list-side-padding: 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 { differenceInHours } from "date-fns/esm";
import { import {
HassServiceTarget, HassServiceTarget,
@ -6,6 +6,7 @@ import {
} from "home-assistant-js-websocket/dist/types"; } from "home-assistant-js-websocket/dist/types";
import { LitElement, PropertyValues, css, html } from "lit"; import { LitElement, PropertyValues, css, html } from "lit";
import { property, query, state } from "lit/decorators"; import { property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { ensureArray } from "../../common/array/ensure-array"; import { ensureArray } from "../../common/array/ensure-array";
import { storage } from "../../common/decorators/storage"; import { storage } from "../../common/decorators/storage";
import { navigate } from "../../common/navigate"; import { navigate } from "../../common/navigate";
@ -15,7 +16,6 @@ import {
extractSearchParamsObject, extractSearchParamsObject,
removeSearchParam, removeSearchParam,
} from "../../common/url/search-params"; } from "../../common/url/search-params";
import { computeRTL } from "../../common/util/compute_rtl";
import { MIN_TIME_BETWEEN_UPDATES } from "../../components/chart/ha-chart-base"; import { MIN_TIME_BETWEEN_UPDATES } from "../../components/chart/ha-chart-base";
import "../../components/chart/state-history-charts"; import "../../components/chart/state-history-charts";
import type { StateHistoryCharts } from "../../components/chart/state-history-charts"; import type { StateHistoryCharts } from "../../components/chart/state-history-charts";
@ -45,8 +45,8 @@ import {
HistoryStates, HistoryStates,
EntityHistoryState, EntityHistoryState,
LineChartUnit, LineChartUnit,
LineChartEntity,
computeGroupKey, computeGroupKey,
LineChartState,
} from "../../data/history"; } from "../../data/history";
import { fetchStatistics, Statistics } from "../../data/recorder"; import { fetchStatistics, Statistics } from "../../data/recorder";
import { getSensorNumericDeviceClasses } from "../../data/sensor"; import { getSensorNumericDeviceClasses } from "../../data/sensor";
@ -54,6 +54,7 @@ import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { haStyle } from "../../resources/styles"; import { haStyle } from "../../resources/styles";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { fileDownload } from "../../util/file_download"; import { fileDownload } from "../../util/file_download";
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
class HaPanelHistory extends SubscribeMixin(LitElement) { class HaPanelHistory extends SubscribeMixin(LitElement) {
@property({ attribute: false }) hass!: HomeAssistant; @property({ attribute: false }) hass!: HomeAssistant;
@ -71,7 +72,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
state: true, state: true,
subscribe: false, subscribe: false,
}) })
private _targetPickerValue?: HassServiceTarget; private _targetPickerValue: HassServiceTarget = {};
@state() private _isLoading = false; @state() private _isLoading = false;
@ -138,6 +139,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
} }
protected render() { protected render() {
const entitiesSelected = this._getEntityIds().length > 0;
return html` return html`
<ha-top-app-bar-fixed> <ha-top-app-bar-fixed>
${this._showBack ${this._showBack
@ -155,7 +157,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
></ha-menu-button> ></ha-menu-button>
`} `}
<div slot="title">${this.hass.localize("panel.history")}</div> <div slot="title">${this.hass.localize("panel.history")}</div>
${this._targetPickerValue ${entitiesSelected
? html` ? html`
<ha-icon-button <ha-icon-button
slot="actionItems" slot="actionItems"
@ -166,17 +168,10 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
></ha-icon-button> ></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 <ha-icon-button
slot="actionItems" slot="actionItems"
@click=${this._downloadHistory} @click=${this._downloadHistory}
.disabled=${this._isLoading || !this._targetPickerValue} .disabled=${this._isLoading}
.path=${mdiDownload} .path=${mdiDownload}
.label=${this.hass.localize("ui.panel.history.download_data")} .label=${this.hass.localize("ui.panel.history.download_data")}
></ha-icon-button> ></ha-icon-button>
@ -203,7 +198,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
? html`<div class="progress-wrapper"> ? html`<div class="progress-wrapper">
<ha-circular-progress indeterminate></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
</div>` </div>`
: !this._targetPickerValue : !entitiesSelected
? html`<div class="start-search"> ? html`<div class="start-search">
${this.hass.localize("ui.panel.history.start_search")} ${this.hass.localize("ui.panel.history.start_search")}
</div>` </div>`
@ -227,50 +222,83 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
): HistoryResult { ): HistoryResult {
const result: HistoryResult = { ...historyResult, line: [] }; const result: HistoryResult = { ...historyResult, line: [] };
const keys = new Set( const lookup: Record<
historyResult.line string,
.map((i) => computeGroupKey(i.unit, i.device_class, true)) { historyItem?: LineChartUnit; ltsItem?: LineChartUnit }
.concat( > = {};
ltsResult.line.map((i) =>
computeGroupKey(i.unit, i.device_class, true) for (const item of historyResult.line) {
) const key = computeGroupKey(item.unit, item.device_class, true);
) if (key) {
); lookup[key] = {
keys.forEach((key) => { historyItem: item,
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 for (const item of ltsResult.line) {
); const key = computeGroupKey(item.unit, item.device_class, true);
if (historyItem && ltsItem) { if (!key) {
const newLineItem: LineChartUnit = { ...historyItem, data: [] }; continue;
const entities = new Set( }
historyItem.data if (key in lookup) {
.map((d) => d.entity_id) lookup[key].ltsItem = item;
.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);
} else { } 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. // Only one result has data for this item, so just push it directly instead of merging.
result.line.push(historyItem || ltsItem!); 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; return result;
} }
@ -342,114 +370,90 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
protected updated(changedProps: PropertyValues) { protected updated(changedProps: PropertyValues) {
if ( if (
this._targetPickerValue && changedProps.has("_startDate") ||
(changedProps.has("_startDate") || changedProps.has("_endDate") ||
changedProps.has("_endDate") || changedProps.has("_targetPickerValue") ||
changedProps.has("_targetPickerValue") || (!this._stateHistory &&
(!this._stateHistory && (changedProps.has("_deviceEntityLookup") ||
(changedProps.has("_deviceEntityLookup") || changedProps.has("_areaEntityLookup") ||
changedProps.has("_areaEntityLookup") || changedProps.has("_areaDeviceLookup")))
changedProps.has("_areaDeviceLookup"))))
) { ) {
this._getHistory(); this._getHistory();
this._getStats(); 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() { private _removeAll() {
this._targetPickerValue = undefined; this._targetPickerValue = {};
this._updatePath(); this._updatePath();
} }
private async _getStats() { private async _getStats() {
const entityIds = this._getEntityIds(); const statisticIds = this._getEntityIds();
if (!entityIds) {
if (statisticIds.length === 0) {
this._statisticsHistory = undefined;
return; return;
} }
this._getStatistics(entityIds);
}
private async _getStatistics(statisticIds: string[]): Promise<void> { const statistics = await fetchStatistics(
try { this.hass!,
const statistics = await fetchStatistics( this._startDate,
this.hass!, this._endDate,
this._startDate, statisticIds,
this._endDate, "hour",
statisticIds, undefined,
"hour", ["mean", "state"]
undefined, );
["mean", "state"]
);
// Maintain the statistic id ordering // Maintain the statistic id ordering
const orderedStatistics: Statistics = {}; const orderedStatistics: Statistics = {};
statisticIds.forEach((id) => { statisticIds.forEach((id) => {
if (id in statistics) { if (id in statistics) {
orderedStatistics[id] = statistics[id]; 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() { private async _getHistory() {
if (!this._targetPickerValue) {
return;
}
this._isLoading = true;
const entityIds = this._getEntityIds(); const entityIds = this._getEntityIds();
if (entityIds === undefined) { if (entityIds.length === 0) {
this._isLoading = false;
this._stateHistory = undefined; this._stateHistory = undefined;
return; return;
} }
if (entityIds.length === 0) { this._isLoading = true;
this._isLoading = false;
this._stateHistory = { line: [], timeline: [] };
return;
}
if (this._subscribed) { if (this._subscribed) {
this._unsubscribeHistory(); this._unsubscribeHistory();
@ -512,84 +516,100 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
} }
} }
private _getEntityIds(): string[] | undefined { private _getEntityIds(): string[] {
if ( return this.__getEntityIds(
!this._targetPickerValue || this._targetPickerValue,
this._deviceEntityLookup === undefined || this._deviceEntityLookup,
this._areaEntityLookup === undefined || this._areaEntityLookup,
this._areaDeviceLookup === undefined this._areaDeviceLookup
) { );
return undefined; }
}
const entityIds = new Set<string>(); private __getEntityIds = memoizeOne(
let { (
area_id: searchingAreaId, targetPickerValue: HassServiceTarget,
device_id: searchingDeviceId, deviceEntityLookup: DeviceEntityLookup | undefined,
entity_id: searchingEntityId, areaEntityLookup: AreaEntityLookup | undefined,
} = this._targetPickerValue; 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) { for (const foundEntity of foundEntities) {
if (foundEntity.entity_category === null) { if (foundEntity.entity_category === null) {
entityIds.add(foundEntity.entity_id); entityIds.add(foundEntity.entity_id);
} }
} }
} }
}
const foundDevices = this._areaDeviceLookup[singleSearchingAreaId]; if (searchingEntityId) {
if (!foundDevices?.length) { searchingEntityId = ensureArray(searchingEntityId);
continue; for (const singleSearchingEntityId of searchingEntityId) {
} entityIds.add(singleSearchingEntityId);
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);
}
}
} }
} }
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) { private _dateRangeChanged(ev) {
this._startDate = ev.detail.startDate; this._startDate = ev.detail.startDate;
@ -611,20 +631,18 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
private _updatePath() { private _updatePath() {
const params: Record<string, string> = {}; const params: Record<string, string> = {};
if (this._targetPickerValue) { if (this._targetPickerValue.entity_id) {
if (this._targetPickerValue.entity_id) { params.entity_id = ensureArray(this._targetPickerValue.entity_id).join(
params.entity_id = ensureArray(this._targetPickerValue.entity_id).join( ","
"," );
); }
} if (this._targetPickerValue.area_id) {
if (this._targetPickerValue.area_id) { params.area_id = ensureArray(this._targetPickerValue.area_id).join(",");
params.area_id = ensureArray(this._targetPickerValue.area_id).join(","); }
} if (this._targetPickerValue.device_id) {
if (this._targetPickerValue.device_id) { params.device_id = ensureArray(this._targetPickerValue.device_id).join(
params.device_id = ensureArray(this._targetPickerValue.device_id).join( ","
"," );
);
}
} }
if (this._startDate) { if (this._startDate) {
@ -639,23 +657,35 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
} }
private _downloadHistory() { 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 csv: string[] = ["entity_id,state,last_changed\n"];
const formatDate = (number) => new Date(number).toISOString(); 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) { for (const entity of line.data) {
const entityId = entity.entity_id; const entityId = entity.entity_id;
for (const data of [entity.states, entity.statistics]) {
if (!data) { if (entity.statistics) {
continue; for (const s of entity.statistics) {
}
for (const s of data) {
csv.push(`${entityId},${s.state},${formatDate(s.last_changed)}\n`); 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; const entityId = timeline.entity_id;
for (const s of timeline.data) { for (const s of timeline.data) {
csv.push(`${entityId},${s.state},${formatDate(s.last_changed)}\n`); csv.push(`${entityId},${s.state},${formatDate(s.last_changed)}\n`);
@ -710,6 +740,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
ha-date-range-picker { ha-date-range-picker {
margin-right: 0; margin-right: 0;
margin-inline-end: 0; margin-inline-end: 0;
margin-inline-start: initial;
width: 100%; width: 100%;
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -187,8 +187,6 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
return html` return html`
<ha-card <ha-card
@action=${this._handleAction} @action=${this._handleAction}
@focus=${this.handleRippleFocus}
@blur=${this.handleRippleBlur}
@mousedown=${this.handleRippleActivate} @mousedown=${this.handleRippleActivate}
@mouseup=${this.handleRippleDeactivate} @mouseup=${this.handleRippleDeactivate}
@mouseenter=${this.handleRippleMouseEnter} @mouseenter=${this.handleRippleMouseEnter}
@ -289,16 +287,6 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
this._rippleHandlers.endPress(); this._rippleHandlers.endPress();
} }
@eventOptions({ passive: true })
private handleRippleFocus() {
this._rippleHandlers.startFocus();
}
@eventOptions({ passive: true })
private handleRippleBlur() {
this._rippleHandlers.endFocus();
}
@eventOptions({ passive: true }) @eventOptions({ passive: true })
private handleRippleMouseEnter() { private handleRippleMouseEnter() {
this._rippleHandlers.startHover(); this._rippleHandlers.startHover();
@ -352,7 +340,18 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
outline: none; 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 { :host(:active) ha-state-icon {
transform: scale(1.2); transform: scale(1.2);
} }

View File

@ -1,5 +1,4 @@
import { mdiImageFilterCenterFocus } from "@mdi/js"; import { mdiImageFilterCenterFocus } from "@mdi/js";
import { isToday } from "date-fns";
import { HassEntities } from "home-assistant-js-websocket"; import { HassEntities } from "home-assistant-js-websocket";
import { LatLngTuple } from "leaflet"; import { LatLngTuple } from "leaflet";
import { import {
@ -14,12 +13,8 @@ import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { getColorByIndex } from "../../../common/color/colors"; import { getColorByIndex } from "../../../common/color/colors";
import { isComponentLoaded } from "../../../common/config/is_component_loaded"; 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 { computeDomain } from "../../../common/entity/compute_domain";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { deepEqual } from "../../../common/util/deep-equal"; import { deepEqual } from "../../../common/util/deep-equal";
import parseAspectRatio from "../../../common/util/parse-aspect-ratio"; import parseAspectRatio from "../../../common/util/parse-aspect-ratio";
import "../../../components/ha-card"; import "../../../components/ha-card";
@ -391,28 +386,23 @@ class HuiMapCard extends LitElement implements LovelaceCard {
} }
const p = {} as HaMapPathPoint; const p = {} as HaMapPathPoint;
p.point = [latitude, longitude] as LatLngTuple; p.point = [latitude, longitude] as LatLngTuple;
const t = new Date(entityState.lu * 1000); p.timestamp = 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
);
}
points.push(p); 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({ paths.push({
points, points,
name,
fullDatetime: (config.hours_to_show ?? DEFAULT_HOURS_TO_SHOW) > 144,
color: this._getColor(entityId), color: this._getColor(entityId),
gradualOpacity: 0.8, gradualOpacity: 0.8,
}); });

View File

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

View File

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

View File

@ -1,6 +1,5 @@
import { PropertyValues, ReactiveElement } from "lit"; import { PropertyValues, ReactiveElement } from "lit";
import { property } from "lit/decorators"; import { property } from "lit/decorators";
import { computeRTL } from "../../../../common/util/compute_rtl";
import { LovelaceCardConfig } from "../../../../data/lovelace/config/card"; import { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import { HomeAssistant } from "../../../../types"; import { HomeAssistant } from "../../../../types";
import { createCardElement } from "../../create-element/create-card-element"; import { createCardElement } from "../../create-element/create-card-element";
@ -70,13 +69,6 @@ export class HuiCardPreview extends ReactiveElement {
} }
if (changedProperties.has("hass")) { 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) { if (this._element) {
this._element.hass = this.hass; this._element.hass = this.hass;
} }

View File

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

View File

@ -18,7 +18,6 @@ import {
} from "superstruct"; } from "superstruct";
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event"; import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
import { customType } from "../../../../common/structs/is-custom-type"; import { customType } from "../../../../common/structs/is-custom-type";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import "../../../../components/ha-card"; import "../../../../components/ha-card";
import "../../../../components/ha-formfield"; import "../../../../components/ha-formfield";
import "../../../../components/ha-icon"; import "../../../../components/ha-icon";
@ -265,7 +264,6 @@ export class HuiEntitiesCardEditor
.label=${this.hass.localize( .label=${this.hass.localize(
"ui.panel.lovelace.editor.card.entities.show_header_toggle" "ui.panel.lovelace.editor.card.entities.show_header_toggle"
)} )}
.dir=${computeRTLDirection(this.hass)}
> >
<ha-switch <ha-switch
.checked=${this._config!.show_header_toggle !== false} .checked=${this._config!.show_header_toggle !== false}
@ -277,7 +275,6 @@ export class HuiEntitiesCardEditor
.label=${this.hass.localize( .label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.state_color" "ui.panel.lovelace.editor.card.generic.state_color"
)} )}
.dir=${computeRTLDirection(this.hass)}
> >
<ha-switch <ha-switch
.checked=${this._config!.state_color} .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 "../../../components/entity/ha-state-label-badge";
import { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge"; import { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
@ -29,10 +28,6 @@ export class HuiBadgePreview extends HTMLElement {
} }
set hass(hass: HomeAssistant) { set hass(hass: HomeAssistant) {
if (!this._hass || this._hass.language !== hass.language) {
this.style.direction = computeRTL(hass) ? "rtl" : "ltr";
}
this._hass = hass; this._hass = hass;
if (this._element) { if (this._element) {
this._element.hass = hass; this._element.hass = hass;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -62,22 +62,16 @@ export const sidebarEditStyle = css`
position: absolute; position: absolute;
top: 0; top: 0;
right: 4px; right: 4px;
inset-inline-end: 4px;
inset-inline-start: initial;
--mdc-icon-button-size: 40px; --mdc-icon-button-size: 40px;
} }
:host([rtl]) .show-panel {
right: initial;
left: 4px;
}
.hide-panel { .hide-panel {
top: 4px; top: 4px;
right: 8px; right: 8px;
} inset-inline-end: 8px;
inset-inline-start: initial;
:host([rtl]) .hide-panel {
right: initial;
left: 8px;
} }
:host([expanded]) .hide-panel { :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 { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { stateActive } from "../common/entity/state_active"; import { stateActive } from "../common/entity/state_active";
import { computeRTL } from "../common/util/compute_rtl";
import "../components/entity/ha-entity-toggle"; import "../components/entity/ha-entity-toggle";
import "../components/entity/state-info"; import "../components/entity/state-info";
import { haStyle } from "../resources/styles"; import { haStyle } from "../resources/styles";
@ -16,9 +15,6 @@ class StateCardAlert extends LitElement {
@property({ type: Boolean }) public inDialog = false; @property({ type: Boolean }) public inDialog = false;
// property used only in CSS
@property({ type: Boolean, reflect: true }) public rtl = false;
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<div class="horizontal justified layout"> <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 { static get styles(): CSSResultGroup {
return [ return [
haStyle, haStyle,
@ -69,7 +53,6 @@ class StateCardAlert extends LitElement {
overflow-wrap: break-word; overflow-wrap: break-word;
display: flex; display: flex;
align-items: center; align-items: center;
direction: ltr;
} }
ha-entity-toggle { ha-entity-toggle {
margin: -4px -16px -4px 0; 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 { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import { computeDomain } from "../common/entity/compute_domain"; import { computeDomain } from "../common/entity/compute_domain";
import { computeRTL } from "../common/util/compute_rtl";
import "../components/entity/state-info"; import "../components/entity/state-info";
import { isUnavailableState } from "../data/entity"; import { isUnavailableState } from "../data/entity";
import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../data/sensor"; 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 { static get styles(): CSSResultGroup {
return [ return [
haStyle, haStyle,
@ -83,7 +70,6 @@ class StateCardDisplay extends LitElement {
word-break: break-word; word-break: break-word;
display: flex; display: flex;
align-items: center; align-items: center;
direction: ltr;
justify-content: flex-end; justify-content: flex-end;
} }
.state.has-unit_of_measurement { .state.has-unit_of_measurement {

View File

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

View File

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

View File

@ -4625,7 +4625,7 @@
"device_actions": { "device_actions": {
"reinterview_device": "Re-interview device", "reinterview_device": "Re-interview device",
"ping_device": "Ping device", "ping_device": "Ping device",
"open_commissioning_window": "Enable commisisioning mode", "open_commissioning_window": "Share device",
"manage_fabrics": "Manage fabrics", "manage_fabrics": "Manage fabrics",
"view_thread_network": "View Thread network" "view_thread_network": "View Thread network"
}, },
@ -4657,11 +4657,12 @@
"ping_complete": "Ping device complete." "ping_complete": "Ping device complete."
}, },
"open_commissioning_window": { "open_commissioning_window": {
"title": "Enable commissioning mode", "title": "Share device",
"introduction": "Enable commissioning mode on the device to pair it to another Matter controller.", "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.", "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.", "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" "sharing_code": "Sharing code"
} }
}, },
@ -6505,7 +6506,9 @@
"start_search": "Start by selecting an area, device or entity above", "start_search": "Start by selecting an area, device or entity above",
"add_all": "Add all entities", "add_all": "Add all entities",
"remove_all": "Remove all selections", "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": { "tips": {

134
yarn.lock
View File

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