mirror of
https://github.com/home-assistant/frontend.git
synced 2026-02-28 04:17:48 +00:00
Compare commits
12 Commits
dev
...
copilot/ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb5c61143f | ||
|
|
4ffaf0271a | ||
|
|
8852843083 | ||
|
|
3a9795e9c8 | ||
|
|
690f17c7b1 | ||
|
|
9c30f39638 | ||
|
|
6c17c95581 | ||
|
|
8c424e8c89 | ||
|
|
1ce0879d37 | ||
|
|
b398994607 | ||
|
|
ffbe4f1de6 | ||
|
|
7d408bb7d3 |
@@ -515,14 +515,6 @@ export class DemoHaAdaptiveDialog extends LitElement {
|
||||
<td><code>--ha-dialog-surface-background</code></td>
|
||||
<td>Dialog/sheet background color.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-dialog-surface-backdrop-filter</code></td>
|
||||
<td>Dialog/sheet surface backdrop filter.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--dialog-box-shadow</code></td>
|
||||
<td>Dialog surface box shadow (dialog mode only).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-dialog-border-radius</code></td>
|
||||
<td>Border radius of the dialog surface (dialog mode only).</td>
|
||||
@@ -535,34 +527,6 @@ export class DemoHaAdaptiveDialog extends LitElement {
|
||||
<td><code>--ha-dialog-hide-duration</code></td>
|
||||
<td>Hide animation duration (dialog mode only).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-dialog-scrim-backdrop-filter</code></td>
|
||||
<td>Dialog/sheet scrim backdrop filter.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--dialog-backdrop-filter</code></td>
|
||||
<td>Dialog/sheet scrim backdrop filter (legacy fallback).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--mdc-dialog-scrim-color</code></td>
|
||||
<td>Dialog/sheet scrim color (legacy compatibility).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-bottom-sheet-surface-background</code></td>
|
||||
<td>Bottom sheet background color (sheet mode only).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-bottom-sheet-surface-backdrop-filter</code></td>
|
||||
<td>Bottom sheet surface backdrop filter (sheet mode only).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-bottom-sheet-scrim-backdrop-filter</code></td>
|
||||
<td>Bottom sheet scrim backdrop filter (sheet mode only).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-bottom-sheet-scrim-color</code></td>
|
||||
<td>Bottom sheet scrim color (sheet mode only).</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -380,29 +380,13 @@ export class DemoHaDialog extends LitElement {
|
||||
<td><code>--ha-dialog-surface-background</code></td>
|
||||
<td>Dialog background color.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-dialog-surface-backdrop-filter</code></td>
|
||||
<td>Backdrop filter applied to the dialog surface.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--dialog-box-shadow</code></td>
|
||||
<td>Dialog surface box shadow.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-dialog-border-radius</code></td>
|
||||
<td>Border radius of the dialog surface.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-dialog-scrim-backdrop-filter</code></td>
|
||||
<td>Backdrop filter applied to the dialog scrim.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--dialog-backdrop-filter</code></td>
|
||||
<td>Legacy fallback for the dialog scrim backdrop filter.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--mdc-dialog-scrim-color</code></td>
|
||||
<td>Dialog scrim color (legacy compatibility).</td>
|
||||
<td><code>--dialog-z-index</code></td>
|
||||
<td>Z-index for the dialog.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--dialog-surface-margin-top</code></td>
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
"@webcomponents/scoped-custom-element-registry": "0.0.10",
|
||||
"@webcomponents/webcomponentsjs": "2.8.0",
|
||||
"app-datepicker": "5.1.1",
|
||||
"barcode-detector": "3.1.0",
|
||||
"barcode-detector": "3.0.8",
|
||||
"color-name": "2.1.0",
|
||||
"comlink": "4.4.2",
|
||||
"core-js": "3.48.0",
|
||||
@@ -149,7 +149,7 @@
|
||||
"@babel/plugin-transform-runtime": "7.29.0",
|
||||
"@babel/preset-env": "7.29.0",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.21.10",
|
||||
"@html-eslint/eslint-plugin": "0.57.0",
|
||||
"@html-eslint/eslint-plugin": "0.56.0",
|
||||
"@lokalise/node-api": "15.6.1",
|
||||
"@octokit/auth-oauth-device": "8.0.3",
|
||||
"@octokit/plugin-retry": "8.1.0",
|
||||
@@ -214,7 +214,7 @@
|
||||
"terser-webpack-plugin": "5.3.16",
|
||||
"ts-lit-plugin": "2.0.2",
|
||||
"typescript": "5.9.3",
|
||||
"typescript-eslint": "8.56.1",
|
||||
"typescript-eslint": "8.56.0",
|
||||
"vite-tsconfig-paths": "6.1.1",
|
||||
"vitest": "4.0.18",
|
||||
"webpack-stats-plugin": "1.1.3",
|
||||
|
||||
@@ -133,34 +133,33 @@ const computeStateToPartsFromEntityAttributes = (
|
||||
),
|
||||
});
|
||||
} catch (_err) {
|
||||
// fallback to default numeric formatting below
|
||||
// fallback to default
|
||||
}
|
||||
|
||||
if (parts.length) {
|
||||
const TYPE_MAP: Record<string, ValuePart["type"]> = {
|
||||
integer: "value",
|
||||
group: "value",
|
||||
decimal: "value",
|
||||
fraction: "value",
|
||||
literal: "literal",
|
||||
currency: "unit",
|
||||
};
|
||||
const TYPE_MAP: Record<string, ValuePart["type"]> = {
|
||||
integer: "value",
|
||||
group: "value",
|
||||
decimal: "value",
|
||||
fraction: "value",
|
||||
literal: "literal",
|
||||
currency: "unit",
|
||||
};
|
||||
|
||||
const valueParts: ValuePart[] = [];
|
||||
const valueParts: ValuePart[] = [];
|
||||
|
||||
for (const part of parts) {
|
||||
const type = TYPE_MAP[part.type];
|
||||
if (!type) continue;
|
||||
const last = valueParts[valueParts.length - 1];
|
||||
// Merge consecutive numeric parts (e.g. "1" + "," + "234" + "." + "56" → "1,234.56")
|
||||
if (type === "value" && last?.type === "value") {
|
||||
last.value += part.value;
|
||||
} else {
|
||||
valueParts.push({ type, value: part.value });
|
||||
}
|
||||
for (const part of parts) {
|
||||
const type = TYPE_MAP[part.type];
|
||||
if (!type) continue;
|
||||
const last = valueParts[valueParts.length - 1];
|
||||
// Merge consecutive numeric parts (e.g. "1" + "," + "234" + "." + "56" → "1,234.56")
|
||||
if (type === "value" && last?.type === "value") {
|
||||
last.value += part.value;
|
||||
} else {
|
||||
valueParts.push({ type, value: part.value });
|
||||
}
|
||||
return valueParts;
|
||||
}
|
||||
|
||||
return valueParts;
|
||||
}
|
||||
|
||||
// default processing of numeric values
|
||||
|
||||
@@ -31,18 +31,9 @@ type DialogSheetMode = "dialog" | "bottom-sheet";
|
||||
* @slot footer - Dialog/sheet footer content.
|
||||
*
|
||||
* @cssprop --ha-dialog-surface-background - Dialog/sheet background color.
|
||||
* @cssprop --ha-dialog-surface-backdrop-filter - Dialog/sheet backdrop filter.
|
||||
* @cssprop --dialog-box-shadow - Dialog box shadow (dialog mode only).
|
||||
* @cssprop --ha-dialog-border-radius - Border radius of the dialog surface (dialog mode only).
|
||||
* @cssprop --ha-dialog-show-duration - Show animation duration (dialog mode only).
|
||||
* @cssprop --ha-dialog-hide-duration - Hide animation duration (dialog mode only).
|
||||
* @cssprop --ha-dialog-scrim-backdrop-filter - Dialog/sheet scrim backdrop filter.
|
||||
* @cssprop --dialog-backdrop-filter - Dialog/sheet scrim backdrop filter (legacy).
|
||||
* @cssprop --mdc-dialog-scrim-color - Dialog/sheet scrim color (legacy).
|
||||
* @cssprop --ha-bottom-sheet-surface-background - Bottom sheet background color (sheet mode only).
|
||||
* @cssprop --ha-bottom-sheet-surface-backdrop-filter - Bottom sheet backdrop filter (sheet mode only).
|
||||
* @cssprop --ha-bottom-sheet-scrim-backdrop-filter - Bottom sheet scrim backdrop filter (sheet mode only).
|
||||
* @cssprop --ha-bottom-sheet-scrim-color - Bottom sheet scrim color (sheet mode only).
|
||||
*
|
||||
* @attr {boolean} open - Controls the dialog/sheet open state.
|
||||
* @attr {("alert"|"standard")} type - Dialog type (dialog mode only). Defaults to "standard".
|
||||
|
||||
@@ -25,27 +25,6 @@ const SWIPE_LOCKED_COMPONENTS = new Set([
|
||||
|
||||
const SWIPE_LOCKED_CLASSES = new Set(["volume-slider-container", "forecast"]);
|
||||
|
||||
/**
|
||||
* Home Assistant bottom sheet component.
|
||||
*
|
||||
* @element ha-bottom-sheet
|
||||
* @extends {LitElement}
|
||||
*
|
||||
* @cssprop --ha-bottom-sheet-height - Preferred height of the bottom sheet.
|
||||
* @cssprop --ha-bottom-sheet-max-height - Maximum height of the bottom sheet.
|
||||
* @cssprop --ha-bottom-sheet-max-width - Maximum width of the bottom sheet.
|
||||
* @cssprop --ha-bottom-sheet-border-radius - Top border radius of the bottom sheet.
|
||||
* @cssprop --ha-bottom-sheet-surface-background - Bottom sheet background color.
|
||||
* @cssprop --ha-bottom-sheet-surface-backdrop-filter - Bottom sheet surface backdrop filter.
|
||||
* @cssprop --ha-bottom-sheet-scrim-backdrop-filter - Bottom sheet scrim backdrop filter.
|
||||
* @cssprop --ha-bottom-sheet-scrim-color - Bottom sheet scrim color.
|
||||
*
|
||||
* @cssprop --ha-dialog-surface-background - Bottom sheet background color fallback.
|
||||
* @cssprop --ha-dialog-surface-backdrop-filter - Bottom sheet surface backdrop filter fallback.
|
||||
* @cssprop --ha-dialog-scrim-backdrop-filter - Bottom sheet scrim backdrop filter fallback.
|
||||
* @cssprop --dialog-backdrop-filter - Bottom sheet scrim backdrop filter legacy fallback.
|
||||
* @cssprop --mdc-dialog-scrim-color - Bottom sheet scrim color legacy fallback.
|
||||
*/
|
||||
@customElement("ha-bottom-sheet")
|
||||
export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
@@ -406,26 +385,6 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
||||
transform: var(--dialog-transform);
|
||||
transition: var(--dialog-transition);
|
||||
}
|
||||
wa-drawer::part(dialog)::backdrop {
|
||||
-webkit-backdrop-filter: var(
|
||||
--ha-bottom-sheet-scrim-backdrop-filter,
|
||||
var(
|
||||
--ha-dialog-scrim-backdrop-filter,
|
||||
var(--dialog-backdrop-filter, none)
|
||||
)
|
||||
);
|
||||
backdrop-filter: var(
|
||||
--ha-bottom-sheet-scrim-backdrop-filter,
|
||||
var(
|
||||
--ha-dialog-scrim-backdrop-filter,
|
||||
var(--dialog-backdrop-filter, none)
|
||||
)
|
||||
);
|
||||
background-color: var(
|
||||
--ha-bottom-sheet-scrim-color,
|
||||
var(--mdc-dialog-scrim-color, none)
|
||||
);
|
||||
}
|
||||
wa-drawer::part(body) {
|
||||
max-width: var(--ha-bottom-sheet-max-width);
|
||||
width: 100%;
|
||||
@@ -440,18 +399,7 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
||||
);
|
||||
background-color: var(
|
||||
--ha-bottom-sheet-surface-background,
|
||||
var(
|
||||
--ha-dialog-surface-background,
|
||||
var(--card-background-color, var(--ha-color-surface-default))
|
||||
)
|
||||
);
|
||||
-webkit-backdrop-filter: var(
|
||||
--ha-bottom-sheet-surface-backdrop-filter,
|
||||
var(--ha-dialog-surface-backdrop-filter, none)
|
||||
);
|
||||
backdrop-filter: var(
|
||||
--ha-bottom-sheet-surface-backdrop-filter,
|
||||
var(--ha-dialog-surface-backdrop-filter, none)
|
||||
var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff)),
|
||||
);
|
||||
padding: var(
|
||||
--ha-bottom-sheet-padding,
|
||||
|
||||
@@ -245,7 +245,7 @@ export class HaButton extends Button {
|
||||
}
|
||||
|
||||
.label {
|
||||
overflow: var(--ha-button-label-overflow, hidden);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: var(--ha-space-1) 0;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { mdiMenuDown } from "@mdi/js";
|
||||
import type { TemplateResult } from "lit";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-attribute-icon";
|
||||
import "./ha-dropdown";
|
||||
import "./ha-dropdown-item";
|
||||
import "./ha-icon";
|
||||
@@ -14,10 +16,17 @@ export interface SelectOption {
|
||||
value: string;
|
||||
iconPath?: string;
|
||||
icon?: string;
|
||||
attributeIcon?: {
|
||||
stateObj: HassEntity;
|
||||
attribute: string;
|
||||
attributeValue?: string;
|
||||
};
|
||||
}
|
||||
|
||||
@customElement("ha-control-select-menu")
|
||||
export class HaControlSelectMenu extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean, attribute: "show-arrow" })
|
||||
public showArrow = false;
|
||||
|
||||
@@ -38,9 +47,6 @@ export class HaControlSelectMenu extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public options: SelectOption[] = [];
|
||||
|
||||
@property({ attribute: false })
|
||||
public renderIcon?: (value: string) => TemplateResult<1> | typeof nothing;
|
||||
|
||||
@query("button") private _triggerButton!: HTMLButtonElement;
|
||||
|
||||
public override render() {
|
||||
@@ -88,8 +94,14 @@ export class HaControlSelectMenu extends LitElement {
|
||||
? html`<ha-svg-icon slot="icon" .path=${option.iconPath}></ha-svg-icon>`
|
||||
: option.icon
|
||||
? html`<ha-icon slot="icon" .icon=${option.icon}></ha-icon>`
|
||||
: this.renderIcon
|
||||
? html`<span slot="icon">${this.renderIcon(option.value)}</span>`
|
||||
: option.attributeIcon
|
||||
? html`<ha-attribute-icon
|
||||
slot="icon"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${option.attributeIcon.stateObj}
|
||||
.attribute=${option.attributeIcon.attribute}
|
||||
.attributeValue=${option.attributeIcon.attributeValue}
|
||||
></ha-attribute-icon>`
|
||||
: nothing}
|
||||
${option.label}</ha-dropdown-item
|
||||
>`;
|
||||
@@ -107,20 +119,24 @@ export class HaControlSelectMenu extends LitElement {
|
||||
}
|
||||
|
||||
private _renderIcon() {
|
||||
const value = this.getValueObject(this.options, this.value);
|
||||
const { iconPath, icon, attributeIcon } =
|
||||
this.getValueObject(this.options, this.value) ?? {};
|
||||
const defaultIcon = this.querySelector("[slot='icon']");
|
||||
|
||||
return html`
|
||||
<div class="icon">
|
||||
${value?.iconPath
|
||||
? html`<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${value.iconPath}
|
||||
></ha-svg-icon>`
|
||||
: value?.icon
|
||||
? html`<ha-icon slot="icon" .icon=${value.icon}></ha-icon>`
|
||||
: this.renderIcon && this.value
|
||||
? this.renderIcon(this.value)
|
||||
${iconPath
|
||||
? html`<ha-svg-icon slot="icon" .path=${iconPath}></ha-svg-icon>`
|
||||
: icon
|
||||
? html`<ha-icon slot="icon" .icon=${icon}></ha-icon>`
|
||||
: attributeIcon
|
||||
? html`<ha-attribute-icon
|
||||
slot="icon"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${attributeIcon.stateObj}
|
||||
.attribute=${attributeIcon.attribute}
|
||||
.attributeValue=${attributeIcon.attributeValue}
|
||||
></ha-attribute-icon>`
|
||||
: defaultIcon
|
||||
? html`<slot name="icon"></slot>`
|
||||
: nothing}
|
||||
@@ -156,12 +172,12 @@ export class HaControlSelectMenu extends LitElement {
|
||||
font-size: var(--ha-font-size-m);
|
||||
line-height: 1.4;
|
||||
width: auto;
|
||||
color: var(--primary-text-color);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.select-anchor {
|
||||
border: none;
|
||||
text-align: left;
|
||||
color: var(--primary-text-color);
|
||||
height: var(--control-select-menu-height);
|
||||
padding: var(--control-select-menu-padding);
|
||||
overflow: hidden;
|
||||
|
||||
@@ -52,12 +52,7 @@ type DialogHideEvent = CustomEvent<{ source?: Element }>;
|
||||
* @cssprop --ha-dialog-show-duration - Show animation duration.
|
||||
* @cssprop --ha-dialog-hide-duration - Hide animation duration.
|
||||
* @cssprop --ha-dialog-surface-background - Dialog background color.
|
||||
* @cssprop --ha-dialog-surface-backdrop-filter - Dialog backdrop filter.
|
||||
* @cssprop --dialog-box-shadow - Dialog box shadow.
|
||||
* @cssprop --ha-dialog-border-radius - Border radius of the dialog surface.
|
||||
* @cssprop --ha-dialog-scrim-backdrop-filter - Dialog scrim backdrop filter.
|
||||
* @cssprop --dialog-backdrop-filter - Dialog scrim backdrop filter (legacy).
|
||||
* @cssprop --mdc-dialog-scrim-color - Dialog scrim color (legacy).
|
||||
* @cssprop --dialog-surface-margin-top - Top margin for the dialog surface.
|
||||
*
|
||||
* @attr {boolean} open - Controls the dialog open state.
|
||||
@@ -276,6 +271,10 @@ export class HaDialog extends ScrollableFadeMixin(LitElement) {
|
||||
--spacing: var(--dialog-content-padding, var(--ha-space-6));
|
||||
--show-duration: var(--ha-dialog-show-duration, 200ms);
|
||||
--hide-duration: var(--ha-dialog-hide-duration, 200ms);
|
||||
--ha-dialog-surface-background: var(
|
||||
--card-background-color,
|
||||
var(--ha-color-surface-default)
|
||||
);
|
||||
--wa-color-surface-raised: var(
|
||||
--ha-dialog-surface-background,
|
||||
var(--card-background-color, var(--ha-color-surface-default))
|
||||
@@ -306,12 +305,6 @@ export class HaDialog extends ScrollableFadeMixin(LitElement) {
|
||||
}
|
||||
|
||||
wa-dialog::part(dialog) {
|
||||
-webkit-backdrop-filter: var(
|
||||
--ha-dialog-surface-backdrop-filter,
|
||||
none
|
||||
);
|
||||
backdrop-filter: var(--ha-dialog-surface-backdrop-filter, none);
|
||||
box-shadow: var(--dialog-box-shadow, var(--wa-shadow-l));
|
||||
color: var(--primary-text-color);
|
||||
min-width: var(--width, var(--full-width));
|
||||
max-width: var(--width, var(--full-width));
|
||||
@@ -341,18 +334,6 @@ export class HaDialog extends ScrollableFadeMixin(LitElement) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
wa-dialog::part(dialog)::backdrop {
|
||||
-webkit-backdrop-filter: var(
|
||||
--ha-dialog-scrim-backdrop-filter,
|
||||
var(--dialog-backdrop-filter, none)
|
||||
);
|
||||
backdrop-filter: var(
|
||||
--ha-dialog-scrim-backdrop-filter,
|
||||
var(--dialog-backdrop-filter, none)
|
||||
);
|
||||
background-color: var(--mdc-dialog-scrim-color, none);
|
||||
}
|
||||
|
||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||
:host([type="standard"]) {
|
||||
--ha-dialog-border-radius: 0;
|
||||
|
||||
@@ -103,6 +103,10 @@ export class HaGenericPicker extends PickerMixin(LitElement) {
|
||||
|
||||
@property({ attribute: "selected-section" }) public selectedSection?: string;
|
||||
|
||||
@property({ attribute: false }) public selectedValue?: string;
|
||||
|
||||
@property({ attribute: false }) public popoverAnchor?: Element | null;
|
||||
|
||||
@property({ type: Boolean, attribute: "use-top-label" })
|
||||
public useTopLabel = false;
|
||||
|
||||
@@ -227,7 +231,8 @@ export class HaGenericPicker extends PickerMixin(LitElement) {
|
||||
without-arrow
|
||||
distance="-4"
|
||||
.placement=${this.popoverPlacement}
|
||||
for="picker"
|
||||
.for=${this.popoverAnchor ? null : "picker"}
|
||||
.anchor=${this.popoverAnchor ?? null}
|
||||
auto-size="vertical"
|
||||
auto-size-padding="16"
|
||||
@wa-after-show=${this._dialogOpened}
|
||||
@@ -267,6 +272,7 @@ export class HaGenericPicker extends PickerMixin(LitElement) {
|
||||
.sections=${this.sections}
|
||||
.sectionTitleFunction=${this.sectionTitleFunction}
|
||||
.selectedSection=${this.selectedSection}
|
||||
.selectedValue=${this.selectedValue}
|
||||
.searchKeys=${this.searchKeys}
|
||||
.customValueLabel=${this.customValueLabel}
|
||||
></ha-picker-combo-box>
|
||||
@@ -345,6 +351,7 @@ export class HaGenericPicker extends PickerMixin(LitElement) {
|
||||
this._opened = false;
|
||||
this._pickerWrapperOpen = false;
|
||||
this._unsubscribeTinyKeys?.();
|
||||
fireEvent(this, "picker-closed");
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
@@ -476,4 +483,8 @@ declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-generic-picker": HaGenericPicker;
|
||||
}
|
||||
|
||||
interface HASSDomEvents {
|
||||
"picker-closed": undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,6 @@ export class HaIconButton extends LitElement {
|
||||
);
|
||||
--wa-color-on-normal: currentColor;
|
||||
--wa-color-fill-quiet: transparent;
|
||||
--ha-button-label-overflow: visible;
|
||||
}
|
||||
ha-button::after {
|
||||
content: "";
|
||||
|
||||
@@ -109,6 +109,8 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
|
||||
@property() public value?: string;
|
||||
|
||||
@property({ attribute: false }) public selectedValue?: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
public searchKeys?: FuseWeightedKey[];
|
||||
|
||||
@@ -256,7 +258,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
.renderItem=${this._renderItem}
|
||||
style="min-height: 36px;"
|
||||
class=${this._listScrolled ? "scrolled" : ""}
|
||||
.layout=${this.value && this._valuePinned
|
||||
.layout=${this._selectedValue && this._valuePinned
|
||||
? {
|
||||
pin: {
|
||||
index: this._getInitialSelectedIndex(),
|
||||
@@ -418,7 +420,9 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
const renderer = this.rowRenderer || DEFAULT_ROW_RENDERER;
|
||||
return html`<div
|
||||
id=${`list-item-${index}`}
|
||||
class="combo-box-row ${this._value === item.id ? "current-value" : ""}"
|
||||
class="combo-box-row ${this._selectedValue === item.id
|
||||
? "current-value"
|
||||
: ""}"
|
||||
.value=${item.id}
|
||||
.index=${index}
|
||||
@click=${this._valueSelected}
|
||||
@@ -433,8 +437,8 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
this._listScrolled = top > 0;
|
||||
}
|
||||
|
||||
private get _value() {
|
||||
return this.value || "";
|
||||
private get _selectedValue() {
|
||||
return this.selectedValue || this.value || "";
|
||||
}
|
||||
|
||||
private _valueSelected = (ev: MouseEvent) => {
|
||||
@@ -771,14 +775,14 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
typeof item === "string" ? item : item?.id;
|
||||
|
||||
private _getInitialSelectedIndex() {
|
||||
if (!this.virtualizerElement || this._search || !this.value) {
|
||||
if (!this.virtualizerElement || this._search || !this._selectedValue) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const index = this.virtualizerElement.items.findIndex(
|
||||
(item) =>
|
||||
typeof item !== "string" &&
|
||||
(item as PickerComboBoxItem).id === this.value
|
||||
(item as PickerComboBoxItem).id === this._selectedValue
|
||||
);
|
||||
|
||||
if (index === -1) {
|
||||
|
||||
@@ -7,10 +7,11 @@ import Fuse from "fuse.js";
|
||||
import type { HassServiceTarget } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing, unsafeCSS } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { ensureArray } from "../common/array/ensure-array";
|
||||
import type { HASSDomEvent } from "../common/dom/fire_event";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { isValidEntityId } from "../common/entity/valid_entity_id";
|
||||
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
||||
@@ -42,6 +43,7 @@ import {
|
||||
deviceMeetsFilter,
|
||||
entityRegMeetsFilter,
|
||||
getTargetComboBoxItemType,
|
||||
type TargetItem,
|
||||
type TargetType,
|
||||
type TargetTypeFloorless,
|
||||
} from "../data/target";
|
||||
@@ -57,6 +59,7 @@ import type { HomeAssistant, ValueChangedEvent } from "../types";
|
||||
import { brandsUrl } from "../util/brands-url";
|
||||
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
||||
import "./ha-generic-picker";
|
||||
import type { HaGenericPicker } from "./ha-generic-picker";
|
||||
import type { PickerComboBoxItem } from "./ha-picker-combo-box";
|
||||
import "./ha-svg-icon";
|
||||
import "./ha-tree-indicator";
|
||||
@@ -65,6 +68,12 @@ import "./target-picker/ha-target-picker-value-chip";
|
||||
|
||||
const SEPARATOR = "________";
|
||||
const CREATE_ID = "___create-new-entity___";
|
||||
const isTargetType = (value: string): value is TargetType =>
|
||||
value === "entity" ||
|
||||
value === "device" ||
|
||||
value === "area" ||
|
||||
value === "label" ||
|
||||
value === "floor";
|
||||
|
||||
@customElement("ha-target-picker")
|
||||
export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
@@ -106,13 +115,19 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
|
||||
@state() private _selectedSection?: TargetTypeFloorless;
|
||||
|
||||
@state() private _replaceTarget?: TargetItem;
|
||||
|
||||
@state() private _replaceTargetAnchor?: HTMLElement;
|
||||
|
||||
@state() private _configEntryLookup: Record<string, ConfigEntry> = {};
|
||||
|
||||
@state()
|
||||
@consume({ context: labelsContext, subscribe: true })
|
||||
private _labelRegistry!: LabelRegistryEntry[];
|
||||
|
||||
private _newTarget?: { type: TargetType; id: string };
|
||||
@query("ha-generic-picker") private _picker?: HaGenericPicker;
|
||||
|
||||
private _newTarget?: TargetItem;
|
||||
|
||||
private _getDevicesMemoized = memoizeOne(getDevices);
|
||||
|
||||
@@ -286,6 +301,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
? html`
|
||||
<ha-target-picker-item-group
|
||||
@remove-target-item=${this._handleRemove}
|
||||
@replace-target-item=${this._handleReplace}
|
||||
type="entity"
|
||||
.hass=${this.hass}
|
||||
.items=${{ entity: entityIds }}
|
||||
@@ -301,6 +317,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
? html`
|
||||
<ha-target-picker-item-group
|
||||
@remove-target-item=${this._handleRemove}
|
||||
@replace-target-item=${this._handleReplace}
|
||||
type="device"
|
||||
.hass=${this.hass}
|
||||
.items=${{ device: deviceIds }}
|
||||
@@ -316,6 +333,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
? html`
|
||||
<ha-target-picker-item-group
|
||||
@remove-target-item=${this._handleRemove}
|
||||
@replace-target-item=${this._handleReplace}
|
||||
type="area"
|
||||
.hass=${this.hass}
|
||||
.items=${{
|
||||
@@ -334,6 +352,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
? html`
|
||||
<ha-target-picker-item-group
|
||||
@remove-target-item=${this._handleRemove}
|
||||
@replace-target-item=${this._handleReplace}
|
||||
type="label"
|
||||
.hass=${this.hass}
|
||||
.items=${{ label: labelIds }}
|
||||
@@ -390,9 +409,14 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
)}
|
||||
.sectionTitleFunction=${this._sectionTitleFunction}
|
||||
.selectedSection=${this._selectedSection}
|
||||
.selectedValue=${this._replaceTarget
|
||||
? `${this._replaceTarget.type}${SEPARATOR}${this._replaceTarget.id}`
|
||||
: undefined}
|
||||
.popoverAnchor=${this._replaceTargetAnchor}
|
||||
.rowRenderer=${this._renderRow}
|
||||
.getItems=${this._getItems}
|
||||
@value-changed=${this._targetPicked}
|
||||
@picker-closed=${this._handlePickerClosed}
|
||||
.addButtonLabel=${this.hass.localize(
|
||||
"ui.components.target-picker.add_target"
|
||||
)}
|
||||
@@ -411,34 +435,42 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [type, id] = ev.detail.value.split(SEPARATOR);
|
||||
this._addTarget(id, type as TargetType);
|
||||
const [rawType, id] = value.split(SEPARATOR);
|
||||
|
||||
if (!id || !isTargetType(rawType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._replaceTarget) {
|
||||
this._replaceTargetItem(this._replaceTarget, { type: rawType, id });
|
||||
return;
|
||||
}
|
||||
|
||||
this._addTarget(id, rawType);
|
||||
}
|
||||
|
||||
private _replaceTargetItem(currentTarget: TargetItem, newTarget: TargetItem) {
|
||||
const value = this._replaceTargetInValue(
|
||||
this.value,
|
||||
currentTarget,
|
||||
newTarget
|
||||
);
|
||||
|
||||
if (value === this.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
fireEvent(this, "value-changed", { value });
|
||||
}
|
||||
|
||||
private _addTarget(id: string, type: TargetType) {
|
||||
const typeId = `${type}_id`;
|
||||
const value = this._addTargetToValue(this.value, { type, id });
|
||||
|
||||
if (typeId === "entity_id" && !isValidEntityId(id)) {
|
||||
if (value === this.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
this.value &&
|
||||
this.value[typeId] &&
|
||||
ensureArray(this.value[typeId]).includes(id)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value: this.value
|
||||
? {
|
||||
...this.value,
|
||||
[typeId]: this.value[typeId]
|
||||
? [...ensureArray(this.value[typeId]), id]
|
||||
: id,
|
||||
}
|
||||
: { [typeId]: id },
|
||||
});
|
||||
fireEvent(this, "value-changed", { value });
|
||||
|
||||
this.shadowRoot
|
||||
?.querySelector(
|
||||
@@ -447,6 +479,52 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
?.removeAttribute("collapsed");
|
||||
}
|
||||
|
||||
private _replaceTargetInValue(
|
||||
value: this["value"],
|
||||
currentTarget: TargetItem,
|
||||
newTarget: TargetItem
|
||||
): this["value"] {
|
||||
if (
|
||||
!value ||
|
||||
(currentTarget.type === newTarget.type &&
|
||||
currentTarget.id === newTarget.id)
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const valueWithoutCurrent = this._removeItem(
|
||||
value,
|
||||
currentTarget.type,
|
||||
currentTarget.id
|
||||
);
|
||||
|
||||
return this._addTargetToValue(valueWithoutCurrent, newTarget);
|
||||
}
|
||||
|
||||
private _addTargetToValue(
|
||||
value: this["value"],
|
||||
target: TargetItem
|
||||
): this["value"] {
|
||||
const typeId = `${target.type}_id`;
|
||||
|
||||
if (typeId === "entity_id" && !isValidEntityId(target.id)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value?.[typeId] && ensureArray(value[typeId]).includes(target.id)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return value
|
||||
? {
|
||||
...value,
|
||||
[typeId]: value[typeId]
|
||||
? [...ensureArray(value[typeId]), target.id]
|
||||
: target.id,
|
||||
}
|
||||
: { [typeId]: target.id };
|
||||
}
|
||||
|
||||
private _createNewDomainElement = (domain: string) => {
|
||||
showHelperDetailDialog(this, {
|
||||
domain,
|
||||
@@ -461,14 +539,14 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
});
|
||||
};
|
||||
|
||||
private _handleRemove(ev) {
|
||||
private _handleRemove(ev: HASSDomEvent<HASSDomEvents["remove-target-item"]>) {
|
||||
const { type, id } = ev.detail;
|
||||
fireEvent(this, "value-changed", {
|
||||
value: this._removeItem(this.value, type, id),
|
||||
});
|
||||
}
|
||||
|
||||
private _handleExpand(ev) {
|
||||
private _handleExpand(ev: HASSDomEvent<HASSDomEvents["expand-target-item"]>) {
|
||||
const type = ev.detail.type;
|
||||
const itemId = ev.detail.id;
|
||||
const newAreas: string[] = [];
|
||||
@@ -614,6 +692,40 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
fireEvent(this, "value-changed", { value });
|
||||
}
|
||||
|
||||
private _handleReplace(
|
||||
ev: HASSDomEvent<HASSDomEvents["replace-target-item"]>
|
||||
) {
|
||||
ev.stopPropagation();
|
||||
this._replaceTargetAnchor = ev
|
||||
.composedPath()
|
||||
.find(
|
||||
(node): node is HTMLElement =>
|
||||
node instanceof HTMLElement &&
|
||||
node.tagName === "HA-TARGET-PICKER-ITEM-ROW"
|
||||
);
|
||||
|
||||
const type = ev.detail.type;
|
||||
if (type === "floor") {
|
||||
this._selectedSection = "area";
|
||||
} else if (
|
||||
type === "entity" ||
|
||||
type === "device" ||
|
||||
type === "area" ||
|
||||
type === "label"
|
||||
) {
|
||||
this._selectedSection = type;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
this._replaceTarget = { type, id: ev.detail.id };
|
||||
this._picker?.open();
|
||||
}
|
||||
|
||||
private _handlePickerClosed() {
|
||||
this._replaceTarget = undefined;
|
||||
this._replaceTargetAnchor = undefined;
|
||||
}
|
||||
|
||||
private _addItems(
|
||||
value: this["value"],
|
||||
type: string,
|
||||
@@ -704,6 +816,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
this.includeDomains,
|
||||
this.includeDeviceClasses,
|
||||
this.value,
|
||||
this._replaceTarget,
|
||||
searchString,
|
||||
this._configEntryLookup,
|
||||
this._selectedSection
|
||||
@@ -718,10 +831,16 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
includeDomains: this["includeDomains"],
|
||||
includeDeviceClasses: this["includeDeviceClasses"],
|
||||
targetValue: this["value"],
|
||||
replaceTarget: TargetItem | undefined,
|
||||
searchTerm: string,
|
||||
configEntryLookup: Record<string, ConfigEntry>,
|
||||
filterType?: TargetTypeFloorless
|
||||
) => {
|
||||
const replacingEntityId =
|
||||
replaceTarget?.type === "entity" ? replaceTarget.id : undefined;
|
||||
const replacingDeviceId =
|
||||
replaceTarget?.type === "device" ? replaceTarget.id : undefined;
|
||||
|
||||
const items: (
|
||||
| string
|
||||
| FloorComboBoxItem
|
||||
@@ -739,9 +858,13 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
undefined,
|
||||
undefined,
|
||||
targetValue?.entity_id
|
||||
? ensureArray(targetValue.entity_id)
|
||||
? ensureArray(targetValue.entity_id).filter(
|
||||
(entityId) => entityId !== replacingEntityId
|
||||
)
|
||||
: undefined,
|
||||
replacingEntityId
|
||||
? `entity${SEPARATOR}${replacingEntityId}`
|
||||
: undefined,
|
||||
undefined,
|
||||
`entity${SEPARATOR}`
|
||||
).sort(this._sortBySortingLabel);
|
||||
|
||||
@@ -772,9 +895,11 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
deviceFilter,
|
||||
entityFilter,
|
||||
targetValue?.device_id
|
||||
? ensureArray(targetValue.device_id)
|
||||
? ensureArray(targetValue.device_id).filter(
|
||||
(deviceId) => deviceId !== replacingDeviceId
|
||||
)
|
||||
: undefined,
|
||||
undefined,
|
||||
replacingDeviceId,
|
||||
`device${SEPARATOR}`
|
||||
).sort(this._sortBySortingLabel);
|
||||
|
||||
@@ -810,8 +935,24 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
includeDeviceClasses,
|
||||
deviceFilter,
|
||||
entityFilter,
|
||||
targetValue?.area_id ? ensureArray(targetValue.area_id) : undefined,
|
||||
targetValue?.floor_id ? ensureArray(targetValue.floor_id) : undefined
|
||||
targetValue?.area_id
|
||||
? ensureArray(targetValue.area_id).filter(
|
||||
(areaId) =>
|
||||
areaId !==
|
||||
(replaceTarget?.type === "area"
|
||||
? replaceTarget.id
|
||||
: undefined)
|
||||
)
|
||||
: undefined,
|
||||
targetValue?.floor_id
|
||||
? ensureArray(targetValue.floor_id).filter(
|
||||
(floorId) =>
|
||||
floorId !==
|
||||
(replaceTarget?.type === "floor"
|
||||
? replaceTarget.id
|
||||
: undefined)
|
||||
)
|
||||
: undefined
|
||||
);
|
||||
|
||||
if (searchTerm) {
|
||||
@@ -860,7 +1001,15 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
includeDeviceClasses,
|
||||
deviceFilter,
|
||||
entityFilter,
|
||||
targetValue?.label_id ? ensureArray(targetValue.label_id) : undefined,
|
||||
targetValue?.label_id
|
||||
? ensureArray(targetValue.label_id).filter(
|
||||
(labelId) =>
|
||||
labelId !==
|
||||
(replaceTarget?.type === "label"
|
||||
? replaceTarget.id
|
||||
: undefined)
|
||||
)
|
||||
: undefined,
|
||||
`label${SEPARATOR}`
|
||||
).sort(this._sortBySortingLabel);
|
||||
|
||||
@@ -1111,14 +1260,9 @@ declare global {
|
||||
}
|
||||
|
||||
interface HASSDomEvents {
|
||||
"remove-target-item": {
|
||||
type: string;
|
||||
id: string;
|
||||
};
|
||||
"expand-target-item": {
|
||||
type: string;
|
||||
id: string;
|
||||
};
|
||||
"remove-target-item": TargetItem;
|
||||
"expand-target-item": TargetItem;
|
||||
"replace-target-item": TargetItem;
|
||||
"remove-target-group": string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,8 +131,16 @@ export class HaTargetPickerItemRow extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const replaceable = !this.subEntry && !this.expand;
|
||||
|
||||
return html`
|
||||
<ha-md-list-item type="text" class=${notFound ? "error" : ""}>
|
||||
<ha-md-list-item
|
||||
type=${replaceable ? "button" : "text"}
|
||||
class=${[notFound ? "error" : "", replaceable ? "replaceable" : ""]
|
||||
.filter(Boolean)
|
||||
.join(" ")}
|
||||
@click=${replaceable ? this._replaceItem : undefined}
|
||||
>
|
||||
<div class="icon" slot="start">
|
||||
${this.subEntry
|
||||
? html`
|
||||
@@ -565,7 +573,7 @@ export class HaTargetPickerItemRow extends LitElement {
|
||||
this._domainName = domainToName(this.hass.localize, domain);
|
||||
}
|
||||
|
||||
private _removeItem(ev) {
|
||||
private _removeItem(ev: MouseEvent) {
|
||||
ev.stopPropagation();
|
||||
fireEvent(this, "remove-target-item", {
|
||||
type: this.type,
|
||||
@@ -589,7 +597,16 @@ export class HaTargetPickerItemRow extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _openDetails() {
|
||||
private _replaceItem(ev: MouseEvent) {
|
||||
ev.stopPropagation();
|
||||
fireEvent(this, "replace-target-item", {
|
||||
type: this.type,
|
||||
id: this.itemId,
|
||||
});
|
||||
}
|
||||
|
||||
private _openDetails(ev: MouseEvent) {
|
||||
ev.stopPropagation();
|
||||
showTargetDetailsDialog(this, {
|
||||
title: this._itemData(this.type, this.itemId).name,
|
||||
type: this.type,
|
||||
@@ -626,6 +643,15 @@ export class HaTargetPickerItemRow extends LitElement {
|
||||
color: var(--ha-color-on-warning-normal);
|
||||
}
|
||||
|
||||
.replaceable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.replaceable:hover,
|
||||
.replaceable:focus-visible {
|
||||
background-color: var(--ha-color-fill-neutral-quiet-hover);
|
||||
}
|
||||
|
||||
state-badge {
|
||||
color: var(--ha-color-on-neutral-quiet);
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ export class HaTargetPickerValueChip extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _removeItem(ev) {
|
||||
private _removeItem(ev: MouseEvent) {
|
||||
ev.stopPropagation();
|
||||
fireEvent(this, "remove-target-item", {
|
||||
type: this.type,
|
||||
@@ -223,7 +223,7 @@ export class HaTargetPickerValueChip extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _handleExpand(ev) {
|
||||
private _handleExpand(ev: MouseEvent) {
|
||||
ev.stopPropagation();
|
||||
fireEvent(this, "expand-target-item", {
|
||||
type: this.type,
|
||||
|
||||
@@ -16,6 +16,11 @@ export const TARGET_SEPARATOR = "________";
|
||||
export type TargetType = "entity" | "device" | "area" | "label" | "floor";
|
||||
export type TargetTypeFloorless = Exclude<TargetType, "floor">;
|
||||
|
||||
export interface TargetItem {
|
||||
type: TargetType;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface SingleHassServiceTarget {
|
||||
entity_id?: string;
|
||||
device_id?: string;
|
||||
|
||||
@@ -9,7 +9,6 @@ import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/ha-attribute-icon";
|
||||
import "../../../components/ha-control-select-menu";
|
||||
import "../../../components/ha-icon-button-group";
|
||||
import "../../../components/ha-icon-button-toggle";
|
||||
@@ -40,38 +39,6 @@ class MoreInfoClimate extends LitElement {
|
||||
|
||||
@state() private _mainControl: MainControl = "temperature";
|
||||
|
||||
private _renderPresetModeIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
attribute="preset_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
private _renderFanModeIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
attribute="fan_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
private _renderSwingModeIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
attribute="swing_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
private _renderSwingHorizontalModeIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
attribute="swing_horizontal_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues): void {
|
||||
if (
|
||||
changedProps.has("stateObj") &&
|
||||
@@ -238,8 +205,12 @@ class MoreInfoClimate extends LitElement {
|
||||
"preset_mode",
|
||||
mode
|
||||
),
|
||||
attributeIcon: {
|
||||
stateObj,
|
||||
attribute: "preset_mode",
|
||||
attributeValue: mode,
|
||||
},
|
||||
}))}
|
||||
.renderIcon=${this._renderPresetModeIcon}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
@@ -263,8 +234,12 @@ class MoreInfoClimate extends LitElement {
|
||||
"fan_mode",
|
||||
mode
|
||||
),
|
||||
attributeIcon: {
|
||||
stateObj,
|
||||
attribute: "fan_mode",
|
||||
attributeValue: mode,
|
||||
},
|
||||
}))}
|
||||
.renderIcon=${this._renderFanModeIcon}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiFan}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
@@ -288,8 +263,12 @@ class MoreInfoClimate extends LitElement {
|
||||
"swing_mode",
|
||||
mode
|
||||
),
|
||||
attributeIcon: {
|
||||
stateObj,
|
||||
attribute: "swing_mode",
|
||||
attributeValue: mode,
|
||||
},
|
||||
}))}
|
||||
.renderIcon=${this._renderSwingModeIcon}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
@@ -318,9 +297,13 @@ class MoreInfoClimate extends LitElement {
|
||||
"swing_horizontal_mode",
|
||||
mode
|
||||
),
|
||||
attributeIcon: {
|
||||
stateObj,
|
||||
attribute: "swing_horizontal_mode",
|
||||
attributeValue: mode,
|
||||
},
|
||||
})
|
||||
)}
|
||||
.renderIcon=${this._renderSwingHorizontalModeIcon}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
|
||||
@@ -40,22 +40,6 @@ class MoreInfoFan extends LitElement {
|
||||
|
||||
@state() public _presetMode?: string;
|
||||
|
||||
private _renderPresetModeIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
attribute="preset_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
private _renderDirectionIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
attribute="direction"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
private _toggle = () => {
|
||||
const service = this.stateObj?.state === "on" ? "turn_off" : "turn_on";
|
||||
forwardHaptic(this, "light");
|
||||
@@ -208,9 +192,15 @@ class MoreInfoFan extends LitElement {
|
||||
"preset_mode",
|
||||
mode
|
||||
),
|
||||
attributeIcon: this.stateObj
|
||||
? {
|
||||
stateObj: this.stateObj,
|
||||
attribute: "preset_mode",
|
||||
attributeValue: mode,
|
||||
}
|
||||
: undefined,
|
||||
})
|
||||
)}
|
||||
.renderIcon=${this._renderPresetModeIcon}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
@@ -236,8 +226,14 @@ class MoreInfoFan extends LitElement {
|
||||
direction
|
||||
)
|
||||
: direction,
|
||||
attributeIcon: this.stateObj
|
||||
? {
|
||||
stateObj: this.stateObj,
|
||||
attribute: "direction",
|
||||
attributeValue: direction,
|
||||
}
|
||||
: undefined,
|
||||
}))}
|
||||
.renderIcon=${this._renderDirectionIcon}
|
||||
>
|
||||
<ha-attribute-icon
|
||||
slot="icon"
|
||||
|
||||
@@ -23,14 +23,6 @@ class MoreInfoHumidifier extends LitElement {
|
||||
|
||||
@state() public _mode?: string;
|
||||
|
||||
private _renderModeIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
attribute="mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues): void {
|
||||
super.willUpdate(changedProps);
|
||||
if (changedProps.has("stateObj")) {
|
||||
@@ -114,8 +106,14 @@ class MoreInfoHumidifier extends LitElement {
|
||||
mode
|
||||
)
|
||||
: mode,
|
||||
attributeIcon: stateObj
|
||||
? {
|
||||
stateObj,
|
||||
attribute: "mode",
|
||||
attributeValue: mode,
|
||||
}
|
||||
: undefined,
|
||||
})) || []}
|
||||
.renderIcon=${this._renderModeIcon}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
|
||||
@@ -55,14 +55,6 @@ class MoreInfoLight extends LitElement {
|
||||
|
||||
@state() private _mainControl: MainControl = "brightness";
|
||||
|
||||
private _renderEffectIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
attribute="effect"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
protected updated(changedProps: PropertyValues<typeof this>): void {
|
||||
if (changedProps.has("stateObj")) {
|
||||
this._effect = this.stateObj?.attributes.effect;
|
||||
@@ -279,9 +271,15 @@ class MoreInfoLight extends LitElement {
|
||||
effect
|
||||
)
|
||||
: effect,
|
||||
attributeIcon: this.stateObj
|
||||
? {
|
||||
stateObj: this.stateObj,
|
||||
attribute: "effect",
|
||||
attributeValue: effect,
|
||||
}
|
||||
: undefined,
|
||||
})
|
||||
)}
|
||||
.renderIcon=${this._renderEffectIcon}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiCreation}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
|
||||
@@ -24,14 +24,6 @@ class MoreInfoWaterHeater extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public stateObj?: WaterHeaterEntity;
|
||||
|
||||
private _renderOperationModeIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
attribute="operation_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
protected render() {
|
||||
if (!this.stateObj) {
|
||||
return nothing;
|
||||
@@ -93,8 +85,12 @@ class MoreInfoWaterHeater extends LitElement {
|
||||
.map((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass.formatEntityState(stateObj, mode),
|
||||
attributeIcon: {
|
||||
stateObj,
|
||||
attribute: "operation_mode",
|
||||
attributeValue: mode,
|
||||
},
|
||||
}))}
|
||||
.renderIcon=${this._renderOperationModeIcon}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiWaterBoiler}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
|
||||
@@ -2,27 +2,17 @@ import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { computeAttributeNameDisplay } from "../../common/entity/compute_attribute_display";
|
||||
import checkValidDate from "../../common/datetime/check_valid_date";
|
||||
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
|
||||
import "../../components/ha-attribute-value";
|
||||
import "../../components/ha-card";
|
||||
import type { LocalizeKeys } from "../../common/translations/localize";
|
||||
import { computeShownAttributes } from "../../data/entity/entity_attributes";
|
||||
import type { ExtEntityRegistryEntry } from "../../data/entity/entity_registry";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../../components/ha-yaml-editor";
|
||||
|
||||
interface DetailsViewParams {
|
||||
entityId: string;
|
||||
}
|
||||
|
||||
interface DetailEntry {
|
||||
translationKey: LocalizeKeys;
|
||||
value: string;
|
||||
}
|
||||
|
||||
@customElement("ha-more-info-details")
|
||||
class HaMoreInfoDetails extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@@ -31,8 +21,6 @@ class HaMoreInfoDetails extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public params?: DetailsViewParams;
|
||||
|
||||
@property({ attribute: false }) public yamlMode = false;
|
||||
|
||||
@state() private _stateObj?: HassEntity;
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues): void {
|
||||
@@ -49,127 +37,60 @@ class HaMoreInfoDetails extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const { stateEntries, attributes, yamlData } = this._getDetailData(
|
||||
this._stateObj
|
||||
const translatedState = this.hass.formatEntityState(this._stateObj);
|
||||
const detailsAttributes = computeShownAttributes(this._stateObj);
|
||||
const detailsAttributeSet = new Set(detailsAttributes);
|
||||
const builtInAttributes = Object.keys(this._stateObj.attributes).filter(
|
||||
(attribute) => !detailsAttributeSet.has(attribute)
|
||||
);
|
||||
const allAttributes = [...detailsAttributes, ...builtInAttributes];
|
||||
|
||||
return html`
|
||||
<div class="content">
|
||||
${this.yamlMode
|
||||
? html`<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.value=${yamlData}
|
||||
read-only
|
||||
auto-update
|
||||
></ha-yaml-editor>`
|
||||
: html`
|
||||
<section class="section">
|
||||
<h2 class="section-title">
|
||||
${this.hass.localize(
|
||||
"ui.components.entity.entity-state-picker.state"
|
||||
)}
|
||||
</h2>
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<div class="data-group">
|
||||
${stateEntries.map(
|
||||
(entry) =>
|
||||
html`<div class="data-entry">
|
||||
<div class="key">
|
||||
${this.hass.localize(entry.translationKey)}
|
||||
</div>
|
||||
<div class="value">${entry.value}</div>
|
||||
</div>`
|
||||
)}
|
||||
</div>
|
||||
<section class="section">
|
||||
<h2 class="section-title">
|
||||
${this.hass.localize(
|
||||
"ui.components.entity.entity-state-picker.state"
|
||||
)}
|
||||
</h2>
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<div class="attribute-group">
|
||||
<div class="data-entry">
|
||||
<div class="key">
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.translated"
|
||||
)}
|
||||
</div>
|
||||
</ha-card>
|
||||
</section>
|
||||
<div class="value">${translatedState}</div>
|
||||
</div>
|
||||
<div class="data-entry">
|
||||
<div class="key">
|
||||
${this.hass.localize("ui.dialogs.more_info_control.raw")}
|
||||
</div>
|
||||
<div class="value">${this._stateObj.state}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ha-card>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2 class="section-title">
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.attributes"
|
||||
)}
|
||||
</h2>
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<div class="data-group">
|
||||
${this._renderAttributes(attributes)}
|
||||
</div>
|
||||
</div>
|
||||
</ha-card>
|
||||
</section>
|
||||
`}
|
||||
<section class="section">
|
||||
<h2 class="section-title">
|
||||
${this.hass.localize("ui.dialogs.more_info_control.attributes")}
|
||||
</h2>
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<div class="attribute-group">
|
||||
${this._renderAttributes(allAttributes)}
|
||||
</div>
|
||||
</div>
|
||||
</ha-card>
|
||||
</section>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _getDetailData = memoizeOne(
|
||||
(
|
||||
stateObj: HassEntity
|
||||
): {
|
||||
stateEntries: DetailEntry[];
|
||||
attributes: string[];
|
||||
yamlData: {
|
||||
state: {
|
||||
translated: string;
|
||||
raw: string;
|
||||
last_changed: string;
|
||||
last_updated: string;
|
||||
};
|
||||
attributes: Record<string, string>;
|
||||
};
|
||||
} => {
|
||||
const translatedState = this.hass.formatEntityState(stateObj);
|
||||
|
||||
const detailsAttributes = computeShownAttributes(stateObj);
|
||||
const detailsAttributeSet = new Set(detailsAttributes);
|
||||
const builtInAttributes = Object.keys(stateObj.attributes).filter(
|
||||
(attribute) => !detailsAttributeSet.has(attribute)
|
||||
);
|
||||
|
||||
return {
|
||||
stateEntries: [
|
||||
{
|
||||
translationKey: "ui.dialogs.more_info_control.translated",
|
||||
value: translatedState,
|
||||
},
|
||||
{
|
||||
translationKey: "ui.dialogs.more_info_control.raw",
|
||||
value: stateObj.state,
|
||||
},
|
||||
{
|
||||
translationKey: "ui.dialogs.more_info_control.last_changed",
|
||||
value: this._formatTimestamp(stateObj.last_changed),
|
||||
},
|
||||
{
|
||||
translationKey: "ui.dialogs.more_info_control.last_updated",
|
||||
value: this._formatTimestamp(stateObj.last_updated),
|
||||
},
|
||||
],
|
||||
attributes: [...detailsAttributes, ...builtInAttributes],
|
||||
yamlData: {
|
||||
state: {
|
||||
translated: translatedState,
|
||||
raw: stateObj.state,
|
||||
last_changed: stateObj.last_changed,
|
||||
last_updated: stateObj.last_updated,
|
||||
},
|
||||
attributes: stateObj.attributes,
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
private _formatTimestamp(value: string): string {
|
||||
const date = new Date(value);
|
||||
|
||||
return checkValidDate(date)
|
||||
? formatDateTimeWithSeconds(date, this.hass.locale, this.hass.config)
|
||||
: value;
|
||||
}
|
||||
|
||||
private _renderAttributes(attributes: string[]) {
|
||||
if (attributes.length === 0) {
|
||||
return html`<div class="empty">
|
||||
@@ -238,7 +159,7 @@ class HaMoreInfoDetails extends LitElement {
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
}
|
||||
|
||||
.data-group .data-entry:last-of-type {
|
||||
.attribute-group .data-entry:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
mdiChartBoxOutline,
|
||||
mdiClose,
|
||||
mdiCodeBraces,
|
||||
mdiCogOutline,
|
||||
mdiDevices,
|
||||
mdiDotsVertical,
|
||||
@@ -133,8 +132,6 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
|
||||
|
||||
@state() private _infoEditMode = false;
|
||||
|
||||
@state() private _detailsYamlMode = false;
|
||||
|
||||
@state() private _isEscapeEnabled = true;
|
||||
|
||||
@state() private _sensorNumericDeviceClasses?: string[] = [];
|
||||
@@ -185,7 +182,6 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
|
||||
this._parentEntityIds = [];
|
||||
this._entry = undefined;
|
||||
this._infoEditMode = false;
|
||||
this._detailsYamlMode = false;
|
||||
this._initialView = DEFAULT_VIEW;
|
||||
this._currView = DEFAULT_VIEW;
|
||||
this._childView = undefined;
|
||||
@@ -255,7 +251,6 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
|
||||
private _goBack() {
|
||||
if (this._childView) {
|
||||
this._childView = undefined;
|
||||
this._detailsYamlMode = false;
|
||||
return;
|
||||
}
|
||||
if (this._initialView !== this._currView) {
|
||||
@@ -319,10 +314,6 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
|
||||
this._infoEditMode = !this._infoEditMode;
|
||||
}
|
||||
|
||||
private _toggleDetailsYamlMode() {
|
||||
this._detailsYamlMode = !this._detailsYamlMode;
|
||||
}
|
||||
|
||||
private _handleToggleInfoEditModeEvent(ev) {
|
||||
this._infoEditMode = ev.detail;
|
||||
}
|
||||
@@ -646,18 +637,7 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
|
||||
</ha-dropdown-item>
|
||||
</ha-dropdown>
|
||||
`
|
||||
: this._childView?.viewTag === "ha-more-info-details"
|
||||
? html`
|
||||
<ha-icon-button
|
||||
slot="headerActionItems"
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.toggle_yaml_mode"
|
||||
)}
|
||||
.path=${mdiCodeBraces}
|
||||
@click=${this._toggleDetailsYamlMode}
|
||||
></ha-icon-button>
|
||||
`
|
||||
: nothing}
|
||||
: nothing}
|
||||
<div
|
||||
class=${classMap({
|
||||
"content-wrapper": true,
|
||||
@@ -683,7 +663,6 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
|
||||
hass: this.hass,
|
||||
entry: this._entry,
|
||||
params: this._childView.viewParams,
|
||||
yamlMode: this._detailsYamlMode,
|
||||
})}
|
||||
</div>
|
||||
`
|
||||
@@ -752,7 +731,6 @@ export class MoreInfoDialog extends ScrollableFadeMixin(LitElement) {
|
||||
if (changedProps.has("_currView")) {
|
||||
this._childView = undefined;
|
||||
this._infoEditMode = false;
|
||||
this._detailsYamlMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import { showQuickBar } from "../../../dialogs/quick-bar/show-dialog-quick-bar";
|
||||
import { showRestartDialog } from "../../../dialogs/restart/show-dialog-restart";
|
||||
import { showShortcutsDialog } from "../../../dialogs/shortcuts/show-shortcuts-dialog";
|
||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { haStyle, haStyleScrollbar } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { isMac } from "../../../util/is_mac";
|
||||
@@ -255,88 +255,90 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
</ha-dropdown-item>
|
||||
</ha-dropdown>
|
||||
|
||||
<ha-config-section
|
||||
.narrow=${this.narrow}
|
||||
.isWide=${this.isWide}
|
||||
full-width
|
||||
>
|
||||
${repairsIssues.length || canInstallUpdates.length
|
||||
? html`<ha-card outlined>
|
||||
${repairsIssues.length
|
||||
? html`
|
||||
<ha-config-repairs
|
||||
<div class="content ha-scrollbar">
|
||||
<ha-config-section
|
||||
.narrow=${this.narrow}
|
||||
.isWide=${this.isWide}
|
||||
full-width
|
||||
>
|
||||
${repairsIssues.length || canInstallUpdates.length
|
||||
? html`<ha-card outlined>
|
||||
${repairsIssues.length
|
||||
? html`
|
||||
<ha-config-repairs
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.total=${totalRepairIssues}
|
||||
.repairsIssues=${repairsIssues}
|
||||
></ha-config-repairs>
|
||||
${totalRepairIssues > repairsIssues.length
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
href="/config/repairs"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.repairs.more_repairs",
|
||||
{
|
||||
count:
|
||||
totalRepairIssues - repairsIssues.length,
|
||||
}
|
||||
)}
|
||||
>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: ""}
|
||||
${repairsIssues.length && canInstallUpdates.length
|
||||
? html`<hr />`
|
||||
: ""}
|
||||
${canInstallUpdates.length
|
||||
? html`
|
||||
<ha-config-updates
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.total=${totalUpdates}
|
||||
.updateEntities=${canInstallUpdates}
|
||||
.isInstallable=${true}
|
||||
></ha-config-updates>
|
||||
${totalUpdates > canInstallUpdates.length
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
href="/config/updates"
|
||||
label=${this.hass.localize(
|
||||
"ui.panel.config.updates.more_updates",
|
||||
{
|
||||
count:
|
||||
totalUpdates - canInstallUpdates.length,
|
||||
}
|
||||
)}
|
||||
>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: ""}
|
||||
</ha-card>`
|
||||
: ""}
|
||||
${this._pages(
|
||||
this.cloudStatus,
|
||||
isComponentLoaded(this.hass, "cloud"),
|
||||
this.hass.auth.external?.config.hasSettingsScreen
|
||||
).map((categoryPages) =>
|
||||
categoryPages.length === 0
|
||||
? nothing
|
||||
: html`
|
||||
<ha-card outlined>
|
||||
<ha-config-navigation
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.total=${totalRepairIssues}
|
||||
.repairsIssues=${repairsIssues}
|
||||
></ha-config-repairs>
|
||||
${totalRepairIssues > repairsIssues.length
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
href="/config/repairs"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.repairs.more_repairs",
|
||||
{
|
||||
count:
|
||||
totalRepairIssues - repairsIssues.length,
|
||||
}
|
||||
)}
|
||||
>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: ""}
|
||||
${repairsIssues.length && canInstallUpdates.length
|
||||
? html`<hr />`
|
||||
: ""}
|
||||
${canInstallUpdates.length
|
||||
? html`
|
||||
<ha-config-updates
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.total=${totalUpdates}
|
||||
.updateEntities=${canInstallUpdates}
|
||||
.isInstallable=${true}
|
||||
></ha-config-updates>
|
||||
${totalUpdates > canInstallUpdates.length
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
href="/config/updates"
|
||||
label=${this.hass.localize(
|
||||
"ui.panel.config.updates.more_updates",
|
||||
{
|
||||
count:
|
||||
totalUpdates - canInstallUpdates.length,
|
||||
}
|
||||
)}
|
||||
>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: ""}
|
||||
</ha-card>`
|
||||
: ""}
|
||||
${this._pages(
|
||||
this.cloudStatus,
|
||||
isComponentLoaded(this.hass, "cloud"),
|
||||
this.hass.auth.external?.config.hasSettingsScreen
|
||||
).map((categoryPages) =>
|
||||
categoryPages.length === 0
|
||||
? nothing
|
||||
: html`
|
||||
<ha-card outlined>
|
||||
<ha-config-navigation
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.pages=${categoryPages}
|
||||
></ha-config-navigation>
|
||||
</ha-card>
|
||||
`
|
||||
)}
|
||||
<ha-tip .hass=${this.hass}>${this._tip}</ha-tip>
|
||||
</ha-config-section>
|
||||
.pages=${categoryPages}
|
||||
></ha-config-navigation>
|
||||
</ha-card>
|
||||
`
|
||||
)}
|
||||
<ha-tip .hass=${this.hass}>${this._tip}</ha-tip>
|
||||
</ha-config-section>
|
||||
</div>
|
||||
</ha-top-app-bar-fixed>
|
||||
`;
|
||||
}
|
||||
@@ -392,7 +394,36 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
haStyleScrollbar,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
ha-top-app-bar-fixed {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: calc(
|
||||
100vh - var(--header-height, 0px) - var(
|
||||
--safe-area-inset-top,
|
||||
0px
|
||||
) - var(--safe-area-inset-bottom, 0px)
|
||||
);
|
||||
height: calc(
|
||||
100dvh - var(--header-height, 0px) - var(
|
||||
--safe-area-inset-top,
|
||||
0px
|
||||
) - var(--safe-area-inset-bottom, 0px)
|
||||
);
|
||||
padding-bottom: var(--ha-space-5);
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
:host(:not([narrow])) ha-card:last-child {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,6 @@ class DialogSystemLogDetail extends LitElement {
|
||||
<ha-dialog
|
||||
.hass=${this.hass}
|
||||
.open=${this._open}
|
||||
width="large"
|
||||
@closed=${this._dialogClosed}
|
||||
>
|
||||
<span slot="headerTitle">${title}</span>
|
||||
|
||||
@@ -143,7 +143,8 @@ class HaConfigRepairs extends LitElement {
|
||||
}
|
||||
} else if (
|
||||
issue.domain === "vacuum" &&
|
||||
issue.translation_key === "segments_changed"
|
||||
(issue.translation_key === "segments_changed" ||
|
||||
issue.translation_key === "segments_mapping_not_configured")
|
||||
) {
|
||||
const data = await fetchRepairsIssueData(
|
||||
this.hass.connection,
|
||||
|
||||
@@ -670,10 +670,10 @@ export class AssistPipelineDebug extends LitElement {
|
||||
background-color: var(--light-primary-color);
|
||||
color: var(--text-light-primary-color, var(--primary-text-color));
|
||||
direction: var(--direction);
|
||||
}
|
||||
|
||||
.tool_result [slot="header"] {
|
||||
color: var(--text-light-primary-color, var(--primary-text-color));
|
||||
--primary-text-color: var(
|
||||
--text-light-primary-color,
|
||||
var(--primary-text-color)
|
||||
);
|
||||
}
|
||||
|
||||
.message.user,
|
||||
|
||||
@@ -49,14 +49,6 @@ class HuiClimateFanModesCardFeature
|
||||
|
||||
@state() _currentFanMode?: string;
|
||||
|
||||
private _renderFanModeIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="fan_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
@@ -183,8 +175,14 @@ class HuiClimateFanModesCardFeature
|
||||
.value=${this._currentFanMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._renderFanModeIcon}
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
attributeIcon: {
|
||||
stateObj: stateObj,
|
||||
attribute: "fan_mode",
|
||||
attributeValue: option.value,
|
||||
},
|
||||
}))}
|
||||
><ha-svg-icon slot="icon" .path=${mdiFan}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
|
||||
@@ -48,14 +48,6 @@ class HuiClimatePresetModesCardFeature
|
||||
|
||||
@state() _currentPresetMode?: string;
|
||||
|
||||
private _renderPresetModeIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="preset_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
@@ -187,8 +179,14 @@ class HuiClimatePresetModesCardFeature
|
||||
.value=${this._currentPresetMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._renderPresetModeIcon}
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
attributeIcon: {
|
||||
stateObj: stateObj,
|
||||
attribute: "preset_mode",
|
||||
attributeValue: option.value,
|
||||
},
|
||||
}))}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
|
||||
@@ -48,14 +48,6 @@ class HuiClimateSwingHorizontalModesCardFeature
|
||||
|
||||
@state() _currentSwingHorizontalMode?: string;
|
||||
|
||||
private _renderSwingHorizontalModeIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="swing_horizontal_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
@@ -195,8 +187,14 @@ class HuiClimateSwingHorizontalModesCardFeature
|
||||
.value=${this._currentSwingHorizontalMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._renderSwingHorizontalModeIcon}
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
attributeIcon: {
|
||||
stateObj: stateObj,
|
||||
attribute: "swing_horizontal_mode",
|
||||
attributeValue: option.value,
|
||||
},
|
||||
}))}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiArrowOscillating}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
|
||||
@@ -48,14 +48,6 @@ class HuiClimateSwingModesCardFeature
|
||||
|
||||
@state() _currentSwingMode?: string;
|
||||
|
||||
private _renderSwingModeIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="swing_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
@@ -187,8 +179,14 @@ class HuiClimateSwingModesCardFeature
|
||||
.value=${this._currentSwingMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._renderSwingModeIcon}
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
attributeIcon: {
|
||||
stateObj,
|
||||
attribute: "swing_mode",
|
||||
attributeValue: option.value,
|
||||
},
|
||||
}))}
|
||||
><ha-svg-icon slot="icon" .path=${mdiArrowOscillating}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
|
||||
@@ -47,14 +47,6 @@ class HuiFanPresetModesCardFeature
|
||||
|
||||
@state() _currentPresetMode?: string;
|
||||
|
||||
private _renderPresetModeIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="preset_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
@@ -181,8 +173,14 @@ class HuiFanPresetModesCardFeature
|
||||
.value=${this._currentPresetMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._renderPresetModeIcon}
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
attributeIcon: {
|
||||
stateObj: stateObj,
|
||||
attribute: "preset_mode",
|
||||
attributeValue: option.value,
|
||||
},
|
||||
}))}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
|
||||
@@ -48,14 +48,6 @@ class HuiHumidifierModesCardFeature
|
||||
|
||||
@state() _currentMode?: string;
|
||||
|
||||
private _renderModeIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
@@ -182,8 +174,14 @@ class HuiHumidifierModesCardFeature
|
||||
.value=${this._currentMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._renderModeIcon}
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
attributeIcon: {
|
||||
stateObj,
|
||||
attribute: "mode",
|
||||
attributeValue: option.value,
|
||||
},
|
||||
}))}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
|
||||
@@ -49,14 +49,6 @@ class HuiWaterHeaterOperationModeCardFeature
|
||||
|
||||
@state() _currentOperationMode?: OperationMode;
|
||||
|
||||
private _renderOperationModeIcon = (value: string) =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="operation_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
@@ -161,8 +153,14 @@ class HuiWaterHeaterOperationModeCardFeature
|
||||
.value=${this._currentOperationMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._renderOperationModeIcon}
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
attributeIcon: {
|
||||
stateObj: this._stateObj,
|
||||
attribute: "operation_mode",
|
||||
attributeValue: option.value,
|
||||
},
|
||||
}))}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiWaterBoiler}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
|
||||
@@ -218,9 +218,7 @@ function formatTooltip(
|
||||
}
|
||||
// when comparing the first value is offset to match the main period
|
||||
// and the real date is in the third value
|
||||
// find the first param with the real date to handle gap-filled entries
|
||||
const origDate = params.find((p) => p.value?.[2] != null)?.value?.[2];
|
||||
const date = new Date(origDate ?? params[0].value?.[0]);
|
||||
const date = new Date(params[0].value?.[2] ?? params[0].value?.[0]);
|
||||
let period: string;
|
||||
|
||||
if (suggestedPeriod === "month") {
|
||||
|
||||
@@ -6,7 +6,6 @@ import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import "../../../components/ha-spinner";
|
||||
import type { HistoryStates } from "../../../data/history";
|
||||
import { subscribeHistoryStatesTimeWindow } from "../../../data/history";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { findEntities } from "../common/find-entities";
|
||||
@@ -67,8 +66,6 @@ export class HuiGraphHeaderFooter
|
||||
|
||||
private _error?: string;
|
||||
|
||||
private _history?: HistoryStates;
|
||||
|
||||
private _interval?: number;
|
||||
|
||||
private _subscribed?: Promise<(() => Promise<void>) | undefined>;
|
||||
@@ -164,8 +161,24 @@ export class HuiGraphHeaderFooter
|
||||
// Message came in before we had a chance to unload
|
||||
return;
|
||||
}
|
||||
this._history = combinedHistory;
|
||||
this._computeCoordinates();
|
||||
const width = this.clientWidth || this.offsetWidth;
|
||||
// sample to 1 point per hour or 1 point per 5 pixels
|
||||
const maxDetails = Math.max(
|
||||
10,
|
||||
this._config.detail! > 1
|
||||
? Math.max(width / 5, this._config.hours_to_show!)
|
||||
: this._config.hours_to_show!
|
||||
);
|
||||
const useMean = this._config.detail !== 2;
|
||||
const { points } = coordinatesMinimalResponseCompressedState(
|
||||
combinedHistory[this._config.entity],
|
||||
width,
|
||||
width / 5,
|
||||
maxDetails,
|
||||
{ minY: this._config.limits?.min, maxY: this._config.limits?.max },
|
||||
useMean
|
||||
);
|
||||
this._coordinates = points;
|
||||
},
|
||||
this._config.hours_to_show!,
|
||||
[this._config.entity]
|
||||
@@ -177,63 +190,10 @@ export class HuiGraphHeaderFooter
|
||||
this._setRedrawTimer();
|
||||
}
|
||||
|
||||
private _computeCoordinates() {
|
||||
if (!this._history || !this._config) {
|
||||
return;
|
||||
}
|
||||
const entityHistory = this._history[this._config.entity];
|
||||
if (!entityHistory?.length) {
|
||||
return;
|
||||
}
|
||||
const width = this.clientWidth || this.offsetWidth;
|
||||
// sample to 1 point per hour or 1 point per 5 pixels
|
||||
const maxDetails = Math.max(
|
||||
10,
|
||||
this._config.detail! > 1
|
||||
? Math.max(width / 5, this._config.hours_to_show!)
|
||||
: this._config.hours_to_show!
|
||||
);
|
||||
const useMean = this._config.detail !== 2;
|
||||
const { points } = coordinatesMinimalResponseCompressedState(
|
||||
entityHistory,
|
||||
width,
|
||||
width / 5,
|
||||
maxDetails,
|
||||
{ minY: this._config.limits?.min, maxY: this._config.limits?.max },
|
||||
useMean
|
||||
);
|
||||
this._coordinates = points;
|
||||
}
|
||||
|
||||
private _redrawGraph() {
|
||||
if (!this._history || !this._config?.hours_to_show) {
|
||||
return;
|
||||
if (this._coordinates) {
|
||||
this._coordinates = [...this._coordinates];
|
||||
}
|
||||
const entityId = this._config.entity;
|
||||
const entityHistory = this._history[entityId];
|
||||
if (entityHistory?.length) {
|
||||
const purgeBeforeTimestamp =
|
||||
(Date.now() - this._config.hours_to_show * 60 * 60 * 1000) / 1000;
|
||||
let purgedHistory = entityHistory.filter(
|
||||
(entry) => entry.lu >= purgeBeforeTimestamp
|
||||
);
|
||||
if (purgedHistory.length !== entityHistory.length) {
|
||||
if (
|
||||
!purgedHistory.length ||
|
||||
purgedHistory[0].lu !== purgeBeforeTimestamp
|
||||
) {
|
||||
// Preserve the last expired state as the start boundary
|
||||
const lastExpiredState = {
|
||||
...entityHistory[entityHistory.length - purgedHistory.length - 1],
|
||||
};
|
||||
lastExpiredState.lu = purgeBeforeTimestamp;
|
||||
delete lastExpiredState.lc;
|
||||
purgedHistory = [lastExpiredState, ...purgedHistory];
|
||||
}
|
||||
this._history = { ...this._history, [entityId]: purgedHistory };
|
||||
}
|
||||
}
|
||||
this._computeCoordinates();
|
||||
}
|
||||
|
||||
private _setRedrawTimer() {
|
||||
@@ -251,7 +211,6 @@ export class HuiGraphHeaderFooter
|
||||
this._subscribed.then((unsub) => unsub?.());
|
||||
this._subscribed = undefined;
|
||||
}
|
||||
this._history = undefined;
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
|
||||
@@ -631,8 +631,11 @@ class HUIRoot extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleWindowScroll = () => {
|
||||
this.toggleAttribute("scrolled", window.scrollY !== 0);
|
||||
private _handleContainerScroll = () => {
|
||||
this.toggleAttribute(
|
||||
"scrolled",
|
||||
this._viewRoot ? this._viewRoot.scrollTop !== 0 : false
|
||||
);
|
||||
};
|
||||
|
||||
private _locationChanged = () => {
|
||||
@@ -663,7 +666,7 @@ class HUIRoot extends LitElement {
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
window.addEventListener("scroll", this._handleWindowScroll, {
|
||||
this._viewRoot?.addEventListener("scroll", this._handleContainerScroll, {
|
||||
passive: true,
|
||||
});
|
||||
this._handleUrlChanged();
|
||||
@@ -674,7 +677,7 @@ class HUIRoot extends LitElement {
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
window.addEventListener("scroll", this._handleWindowScroll, {
|
||||
this._viewRoot?.addEventListener("scroll", this._handleContainerScroll, {
|
||||
passive: true,
|
||||
});
|
||||
window.addEventListener("popstate", this._handlePopState);
|
||||
@@ -685,10 +688,13 @@ class HUIRoot extends LitElement {
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
window.removeEventListener("scroll", this._handleWindowScroll);
|
||||
this._viewRoot?.removeEventListener("scroll", this._handleContainerScroll);
|
||||
window.removeEventListener("popstate", this._handlePopState);
|
||||
window.removeEventListener("location-changed", this._locationChanged);
|
||||
this.toggleAttribute("scrolled", window.scrollY !== 0);
|
||||
this.toggleAttribute(
|
||||
"scrolled",
|
||||
this._viewRoot ? this._viewRoot.scrollTop !== 0 : false
|
||||
);
|
||||
// Re-enable history scroll restoration when leaving the page
|
||||
window.history.scrollRestoration = "auto";
|
||||
}
|
||||
@@ -821,9 +827,11 @@ class HUIRoot extends LitElement {
|
||||
(this._restoreScroll && this._viewScrollPositions[newSelectView]) ||
|
||||
0;
|
||||
this._restoreScroll = false;
|
||||
requestAnimationFrame(() =>
|
||||
scrollTo({ behavior: "auto", top: position })
|
||||
);
|
||||
requestAnimationFrame(() => {
|
||||
if (this._viewRoot) {
|
||||
this._viewRoot.scrollTo({ behavior: "auto", top: position });
|
||||
}
|
||||
});
|
||||
}
|
||||
this._selectView(newSelectView, force);
|
||||
});
|
||||
@@ -1148,7 +1156,7 @@ class HUIRoot extends LitElement {
|
||||
const path = this.config.views[viewIndex].path || viewIndex;
|
||||
this._navigateToView(path);
|
||||
} else if (!this._editMode) {
|
||||
scrollTo({ behavior: "smooth", top: 0 });
|
||||
this._viewRoot?.scrollTo({ behavior: "smooth", top: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1159,7 +1167,7 @@ class HUIRoot extends LitElement {
|
||||
|
||||
// Save scroll position of current view
|
||||
if (this._curView != null) {
|
||||
this._viewScrollPositions[this._curView] = window.scrollY;
|
||||
this._viewScrollPositions[this._curView] = this._viewRoot?.scrollTop ?? 0;
|
||||
}
|
||||
|
||||
viewIndex = viewIndex === undefined ? 0 : viewIndex;
|
||||
@@ -1467,9 +1475,14 @@ class HUIRoot extends LitElement {
|
||||
hui-view-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
height: calc(
|
||||
100vh - var(--header-height) - var(--safe-area-inset-top) - var(
|
||||
--view-container-padding-top,
|
||||
0px
|
||||
)
|
||||
);
|
||||
box-sizing: border-box;
|
||||
padding-top: calc(
|
||||
margin-top: calc(
|
||||
var(--header-height) + var(--safe-area-inset-top) +
|
||||
var(--view-container-padding-top, 0px)
|
||||
);
|
||||
@@ -1485,8 +1498,6 @@ class HUIRoot extends LitElement {
|
||||
padding-inline-start: var(--safe-area-inset-left);
|
||||
}
|
||||
hui-view-container > * {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
@@ -1494,7 +1505,12 @@ class HUIRoot extends LitElement {
|
||||
* In edit mode we have the tab bar on a new line *
|
||||
*/
|
||||
hui-view-container.has-tab-bar {
|
||||
padding-top: calc(
|
||||
height: calc(
|
||||
100vh - var(--header-height, 56px) - calc(
|
||||
var(--tab-bar-height, 56px) - 2px
|
||||
) - var(--safe-area-inset-top, 0px)
|
||||
);
|
||||
margin-top: calc(
|
||||
var(--header-height, 56px) +
|
||||
calc(var(--tab-bar-height, 56px) - 2px) +
|
||||
var(--safe-area-inset-top, 0px)
|
||||
|
||||
@@ -418,11 +418,15 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
||||
}
|
||||
|
||||
private _toggleView() {
|
||||
// The scroll container is the hui-view-container parent
|
||||
const scrollContainer = this.closest("hui-view-container");
|
||||
const scrollTop = scrollContainer?.scrollTop ?? 0;
|
||||
|
||||
// Save current scroll position
|
||||
if (this._sidebarTabActive) {
|
||||
this._sidebarScrollTop = window.scrollY;
|
||||
this._sidebarScrollTop = scrollTop;
|
||||
} else {
|
||||
this._contentScrollTop = window.scrollY;
|
||||
this._contentScrollTop = scrollTop;
|
||||
}
|
||||
|
||||
this._sidebarTabActive = !this._sidebarTabActive;
|
||||
@@ -438,7 +442,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
||||
const scrollY = this._sidebarTabActive
|
||||
? this._sidebarScrollTop
|
||||
: this._contentScrollTop;
|
||||
window.scrollTo(0, scrollY);
|
||||
scrollContainer?.scrollTo(0, scrollY);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -461,7 +465,6 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
||||
--column-min-width: var(--ha-view-sections-column-min-width, 320px);
|
||||
--top-margin: var(--ha-view-sections-extra-top-margin, 80px);
|
||||
display: block;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
@@ -471,9 +474,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: calc(100% - 2 * var(--row-gap));
|
||||
display: block;
|
||||
padding: var(--row-gap) var(--column-gap);
|
||||
box-sizing: content-box;
|
||||
margin: 0 auto;
|
||||
@@ -506,7 +507,6 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
||||
gap: var(--row-gap) var(--column-gap);
|
||||
padding: var(--row-gap) 0;
|
||||
align-items: flex-start;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.wrapper.has-sidebar .container {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||
import { listenMediaQuery } from "../../../common/dom/media_query";
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import { haStyleScrollbar } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
|
||||
type BackgroundConfig = LovelaceViewConfig["background"];
|
||||
@@ -22,6 +23,7 @@ class HuiViewContainer extends LitElement {
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.classList.add("ha-scrollbar");
|
||||
this._setUpMediaQuery();
|
||||
this._applyTheme();
|
||||
}
|
||||
@@ -74,11 +76,16 @@ class HuiViewContainer extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: relative;
|
||||
}
|
||||
`;
|
||||
static styles = [
|
||||
haStyleScrollbar,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
height: 100%;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -244,17 +244,20 @@ export const haStyleDialogFixedTop = css`
|
||||
`;
|
||||
|
||||
export const haStyleScrollbar = css`
|
||||
.ha-scrollbar::-webkit-scrollbar {
|
||||
.ha-scrollbar::-webkit-scrollbar,
|
||||
:host(.ha-scrollbar)::-webkit-scrollbar {
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.ha-scrollbar::-webkit-scrollbar-thumb {
|
||||
.ha-scrollbar::-webkit-scrollbar-thumb,
|
||||
:host(.ha-scrollbar)::-webkit-scrollbar-thumb {
|
||||
border-radius: var(--ha-border-radius-sm);
|
||||
background: var(--scrollbar-thumb-color);
|
||||
}
|
||||
|
||||
.ha-scrollbar {
|
||||
.ha-scrollbar,
|
||||
:host(.ha-scrollbar) {
|
||||
overflow-y: auto;
|
||||
scrollbar-color: var(--scrollbar-thumb-color) transparent;
|
||||
scrollbar-width: thin;
|
||||
|
||||
@@ -359,8 +359,6 @@ export const darkColorStyles = css`
|
||||
--outline-hover-color: rgba(225, 225, 225, 0.24);
|
||||
--shadow-color: rgba(0, 0, 0, 0.48);
|
||||
|
||||
--scrollbar-thumb-color: rgb(110, 110, 110);
|
||||
|
||||
--mdc-ripple-color: #aaaaaa;
|
||||
--mdc-linear-progress-buffer-color: rgba(255, 255, 255, 0.1);
|
||||
|
||||
|
||||
@@ -51,21 +51,6 @@ export const mainStyles = css`
|
||||
|
||||
/* dialog backdrop filter */
|
||||
--ha-dialog-scrim-backdrop-filter: brightness(68%);
|
||||
|
||||
/* scrollbar */
|
||||
scrollbar-color: var(--scrollbar-thumb-color) transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
html::-webkit-scrollbar {
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
html::-webkit-scrollbar-thumb {
|
||||
border-radius: var(--ha-border-radius-sm);
|
||||
background: var(--scrollbar-thumb-color);
|
||||
border: 3px solid transparent;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -1524,7 +1524,6 @@
|
||||
"settings": "Settings",
|
||||
"edit": "Edit entity",
|
||||
"details": "Details",
|
||||
"toggle_yaml_mode": "Toggle YAML mode",
|
||||
"translated": "Translated",
|
||||
"raw": "Raw",
|
||||
"back_to_info": "Back to info",
|
||||
|
||||
265
yarn.lock
265
yarn.lock
@@ -1990,75 +1990,74 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@html-eslint/core@npm:^0.57.0":
|
||||
version: 0.57.0
|
||||
resolution: "@html-eslint/core@npm:0.57.0"
|
||||
"@html-eslint/core@npm:^0.56.0":
|
||||
version: 0.56.0
|
||||
resolution: "@html-eslint/core@npm:0.56.0"
|
||||
dependencies:
|
||||
"@html-eslint/types": "npm:^0.57.0"
|
||||
"@html-eslint/types": "npm:^0.56.0"
|
||||
eslint: "npm:^9.39.1"
|
||||
html-standard: "npm:^0.0.13"
|
||||
checksum: 10/18b7aaf455969008dc5b79f6fd4b38792d893053e77c098c56a9238fe9b3ac3fa36441a88b908ade71de2f5179d5b4c45f552d7da5beff1d5c8df9cb3fd14e63
|
||||
checksum: 10/0722e7405e56b256c471229dbd0ccb49cf0edd3e5224a087240f1954c74af0a6f289e5be0b83957480f1fa5abf6f7cbad7212134a6872a3fd66e1dc1ad879d66
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@html-eslint/eslint-plugin@npm:0.57.0":
|
||||
version: 0.57.0
|
||||
resolution: "@html-eslint/eslint-plugin@npm:0.57.0"
|
||||
"@html-eslint/eslint-plugin@npm:0.56.0":
|
||||
version: 0.56.0
|
||||
resolution: "@html-eslint/eslint-plugin@npm:0.56.0"
|
||||
dependencies:
|
||||
"@eslint/plugin-kit": "npm:^0.4.1"
|
||||
"@html-eslint/core": "npm:^0.57.0"
|
||||
"@html-eslint/parser": "npm:^0.57.0"
|
||||
"@html-eslint/template-parser": "npm:^0.57.0"
|
||||
"@html-eslint/template-syntax-parser": "npm:^0.57.0"
|
||||
"@html-eslint/types": "npm:^0.57.0"
|
||||
"@rviscomi/capo.js": "npm:^2.1.0"
|
||||
"@html-eslint/core": "npm:^0.56.0"
|
||||
"@html-eslint/parser": "npm:^0.56.0"
|
||||
"@html-eslint/template-parser": "npm:^0.56.0"
|
||||
"@html-eslint/template-syntax-parser": "npm:^0.56.0"
|
||||
"@html-eslint/types": "npm:^0.56.0"
|
||||
peerDependencies:
|
||||
eslint: ">=8.0.0 || ^10.0.0-0"
|
||||
checksum: 10/34cf11eaab3c07436c9c8b2896ed331c3ba5d591db5d8a8ff79d19b174e6e090323eb71e1368ad93d2afa945d4740749c599436a03be1195d4e4a4b0d3d9e9b6
|
||||
checksum: 10/16c5d638045266ca201b5dc154b3706262801668b2edde4f0905bdba9ba2e14b7175eacf9ca7d2ad5a5a03e3369843822d34ee607b6b7fbbeb020ac7f87629fb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@html-eslint/parser@npm:^0.57.0":
|
||||
version: 0.57.0
|
||||
resolution: "@html-eslint/parser@npm:0.57.0"
|
||||
"@html-eslint/parser@npm:^0.56.0":
|
||||
version: 0.56.0
|
||||
resolution: "@html-eslint/parser@npm:0.56.0"
|
||||
dependencies:
|
||||
"@eslint/css-tree": "npm:^3.6.9"
|
||||
"@html-eslint/template-syntax-parser": "npm:^0.57.0"
|
||||
"@html-eslint/types": "npm:^0.57.0"
|
||||
"@html-eslint/template-syntax-parser": "npm:^0.56.0"
|
||||
"@html-eslint/types": "npm:^0.56.0"
|
||||
css-tree: "npm:^3.1.0"
|
||||
es-html-parser: "npm:0.3.1"
|
||||
checksum: 10/d742a26ef8122eab82e35ab27de6fe40cd45d2ac59ade9bed9d16df0da3cd18425c7892fedd245b9d60da2a4fbfb8c6afe05b1b6415033ab8fad996b94be020e
|
||||
checksum: 10/1fead2be97481f461ee7030f4ea9274e5f969540118b4429e3eea361f62138660c1b7538668c7f8d55a8ae6985deff3056968d71bae62b91fb001d4209a24b50
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@html-eslint/template-parser@npm:^0.57.0":
|
||||
version: 0.57.0
|
||||
resolution: "@html-eslint/template-parser@npm:0.57.0"
|
||||
"@html-eslint/template-parser@npm:^0.56.0":
|
||||
version: 0.56.0
|
||||
resolution: "@html-eslint/template-parser@npm:0.56.0"
|
||||
dependencies:
|
||||
"@html-eslint/types": "npm:^0.57.0"
|
||||
"@html-eslint/types": "npm:^0.56.0"
|
||||
es-html-parser: "npm:0.3.1"
|
||||
checksum: 10/160d7f6156f242ed7bd4e7f13d79c350bf5db7fef089ba7b404ef770d80547620e9ee860ead8892a4079668db1cdccf6a6c6b91183a8c478a634314b39efea32
|
||||
checksum: 10/45e41dc8fb46336f40d1b78ef55ee3150d7a1034d393529eb40ce37a7d0a8fa04f6ed8f9a63478ff884d9588b1359b97a799681a37cd485d237dce867b7f9d20
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@html-eslint/template-syntax-parser@npm:^0.57.0":
|
||||
version: 0.57.0
|
||||
resolution: "@html-eslint/template-syntax-parser@npm:0.57.0"
|
||||
"@html-eslint/template-syntax-parser@npm:^0.56.0":
|
||||
version: 0.56.0
|
||||
resolution: "@html-eslint/template-syntax-parser@npm:0.56.0"
|
||||
dependencies:
|
||||
"@html-eslint/types": "npm:^0.57.0"
|
||||
checksum: 10/147d8d15ebc5ea9b75eea04463b9f29cfc476be2e98c5a2129442be6f66d32a9e8bb11a1be13cc3778df25e14d7ed1cd345342951ecec05292545f64bb3d92c6
|
||||
"@html-eslint/types": "npm:^0.56.0"
|
||||
checksum: 10/2cc4f2cbfa58c6381b2542ca86e424a97429326aa0b8b6b09e0a51d27b0e26fd723a4550e4c17536e00cfd506cbd0c61d8fd3dff1c829714c4432406096b4a01
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@html-eslint/types@npm:^0.57.0":
|
||||
version: 0.57.0
|
||||
resolution: "@html-eslint/types@npm:0.57.0"
|
||||
"@html-eslint/types@npm:^0.56.0":
|
||||
version: 0.56.0
|
||||
resolution: "@html-eslint/types@npm:0.56.0"
|
||||
dependencies:
|
||||
"@types/css-tree": "npm:^2.3.11"
|
||||
"@types/estree": "npm:^1.0.6"
|
||||
es-html-parser: "npm:0.3.1"
|
||||
eslint: "npm:^9.39.1"
|
||||
checksum: 10/44d0bde592786a2da966beab13c3d543e6669b1e38b6e5feea0f01121eec928be0081969df3ada1f27c9bd43d6286596afa56a2e075da843251079a940cf0825
|
||||
checksum: 10/5359ccfb8f431ccedfe7d9c0b20d883d19bbb6a24eee925c036c7774d6f2177eda021826abbc6f947b59c2364d7cf25a596591e852c1141000a3fab6ea70acc9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -4258,13 +4257,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rviscomi/capo.js@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "@rviscomi/capo.js@npm:2.1.0"
|
||||
checksum: 10/83ce30106ec6bd4b4791af12556119c568cb9d0ec7550b7b015a8c2c1c49687d8c6605564415d9d85e991581958212109ee5caa0db4166c1d3628033175e80b8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@shoelace-style/animations@npm:^1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "@shoelace-style/animations@npm:1.2.0"
|
||||
@@ -5071,138 +5063,138 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/eslint-plugin@npm:8.56.1":
|
||||
version: 8.56.1
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.56.1"
|
||||
"@typescript-eslint/eslint-plugin@npm:8.56.0":
|
||||
version: 8.56.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.56.0"
|
||||
dependencies:
|
||||
"@eslint-community/regexpp": "npm:^4.12.2"
|
||||
"@typescript-eslint/scope-manager": "npm:8.56.1"
|
||||
"@typescript-eslint/type-utils": "npm:8.56.1"
|
||||
"@typescript-eslint/utils": "npm:8.56.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.56.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.56.0"
|
||||
"@typescript-eslint/type-utils": "npm:8.56.0"
|
||||
"@typescript-eslint/utils": "npm:8.56.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.56.0"
|
||||
ignore: "npm:^7.0.5"
|
||||
natural-compare: "npm:^1.4.0"
|
||||
ts-api-utils: "npm:^2.4.0"
|
||||
peerDependencies:
|
||||
"@typescript-eslint/parser": ^8.56.1
|
||||
"@typescript-eslint/parser": ^8.56.0
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10/669d19cff91fcad5fe34dba97cc8c0c2df3160ae14646759fb23dfd6ffbb861d00d8d081e74d1060d544bfba0ea4d05588c5b73ae104907af26cc18189c0d139
|
||||
checksum: 10/44201eae518c759cf3110f7e0a374372ef22bffa3cca61685aebe916b06bcb5f3ed6ffedba252199dca0006dfc22c54b132cd0337fd15e8c083eda22f9cf6b0c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/parser@npm:8.56.1":
|
||||
version: 8.56.1
|
||||
resolution: "@typescript-eslint/parser@npm:8.56.1"
|
||||
"@typescript-eslint/parser@npm:8.56.0":
|
||||
version: 8.56.0
|
||||
resolution: "@typescript-eslint/parser@npm:8.56.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager": "npm:8.56.1"
|
||||
"@typescript-eslint/types": "npm:8.56.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.56.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.56.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.56.0"
|
||||
"@typescript-eslint/types": "npm:8.56.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.56.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.56.0"
|
||||
debug: "npm:^4.4.3"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10/280b041a69153caf9e721b307781830483dd39d881b02d993156635bd8600e30e6a816aaead8bdd662ae5149b8870aef7b3823d3b98ec974d924c23a786fb6d9
|
||||
checksum: 10/9bdb2c7915665a1031499049974997020bbc34557803a8c3718b323d5583a3fdfc27797ec714b617743225c8dce9ab589eb442b71c435b464ad45365a6fbba57
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/project-service@npm:8.56.1":
|
||||
version: 8.56.1
|
||||
resolution: "@typescript-eslint/project-service@npm:8.56.1"
|
||||
"@typescript-eslint/project-service@npm:8.56.0":
|
||||
version: 8.56.0
|
||||
resolution: "@typescript-eslint/project-service@npm:8.56.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/tsconfig-utils": "npm:^8.56.1"
|
||||
"@typescript-eslint/types": "npm:^8.56.1"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:^8.56.0"
|
||||
"@typescript-eslint/types": "npm:^8.56.0"
|
||||
debug: "npm:^4.4.3"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10/5e7fdc95aebcefc72fec77806bb0821a9a59e5e88f86d72b15ad011eb6110da05419b803875f021716a219fc7fb8517331a6736364344c8613a90209539a6d32
|
||||
checksum: 10/b46cc78bfb50ee84cb12e2e99c1a3d9606161980cf56ba33be6244ccff2ba4c78ceec46706ad597508fda167e0f965d849c1c516400ad41259027be3fbd815eb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/scope-manager@npm:8.56.1":
|
||||
version: 8.56.1
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.56.1"
|
||||
"@typescript-eslint/scope-manager@npm:8.56.0":
|
||||
version: 8.56.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.56.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.56.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.56.1"
|
||||
checksum: 10/f358cf8bd32952eed005d4f34c1e95805baefe35abee96d866222b0eff8027cc02f831cee04b308707d74db2b415437a134191207b4213ee8acbc6d67a435616
|
||||
"@typescript-eslint/types": "npm:8.56.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.56.0"
|
||||
checksum: 10/3662355120ea8e21ce01c999decbd2a09fe4edb1c01e376fe347952d968ecfedff99b9484334e133e41284a15f2e1bc8efd490b1e73a16980614445c25b07b0d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@npm:8.56.1, @typescript-eslint/tsconfig-utils@npm:^8.56.1":
|
||||
version: 8.56.1
|
||||
resolution: "@typescript-eslint/tsconfig-utils@npm:8.56.1"
|
||||
"@typescript-eslint/tsconfig-utils@npm:8.56.0, @typescript-eslint/tsconfig-utils@npm:^8.56.0":
|
||||
version: 8.56.0
|
||||
resolution: "@typescript-eslint/tsconfig-utils@npm:8.56.0"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10/d509f1ae4b14969173e498db6d15c833b6407db456c7fca9e25396798a35014229a73754691f353c4a99f5f0bbb4535b4144f42f84e596645a16d88a2022135f
|
||||
checksum: 10/b1834aeffcdc07835eae0bf52aca573cba7e6528b5c1483e9b1f7f4f9e1f6450a8650796be11140e0437caf7eb1b0f9711c22989c8294547534f12614a759760
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/type-utils@npm:8.56.1":
|
||||
version: 8.56.1
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.56.1"
|
||||
"@typescript-eslint/type-utils@npm:8.56.0":
|
||||
version: 8.56.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.56.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.56.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.56.1"
|
||||
"@typescript-eslint/utils": "npm:8.56.1"
|
||||
"@typescript-eslint/types": "npm:8.56.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.56.0"
|
||||
"@typescript-eslint/utils": "npm:8.56.0"
|
||||
debug: "npm:^4.4.3"
|
||||
ts-api-utils: "npm:^2.4.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10/2b07c674c26d797d05c05779ac5c89761b6b96680ecaf01440957727d12c6d06a2e48f0b139e45752eb4b53bf13c03940628656c519d362082b716d6a0ece6d9
|
||||
checksum: 10/f272b9acc004f125cbf0df18265a43ba50cd3666262afc663585acdd1be6b6b30724bd8cf4cd5aa2757b7f10ceafa92fd1af30c1931fb22ac38521eda7f79c89
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/types@npm:8.56.1, @typescript-eslint/types@npm:^8.56.1":
|
||||
version: 8.56.1
|
||||
resolution: "@typescript-eslint/types@npm:8.56.1"
|
||||
checksum: 10/4bcffab5b0fd48adb731fcade86a776ca4a66e229defa5a282b58ba9c95af16ffc459a7d188e27c988a35be1f6fb5b812f9cf0952692eac38d5b3e87daafb20a
|
||||
"@typescript-eslint/types@npm:8.56.0, @typescript-eslint/types@npm:^8.56.0":
|
||||
version: 8.56.0
|
||||
resolution: "@typescript-eslint/types@npm:8.56.0"
|
||||
checksum: 10/d7549535c99d9202742bf0191bcc2822c2d18a03e206be9ad5a6f6b0902de7381c93e8c238754fe5d1dfdcc22d7e3bbafa032f63ba165d6dc03e180f84b138f9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:8.56.1":
|
||||
version: 8.56.1
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.56.1"
|
||||
"@typescript-eslint/typescript-estree@npm:8.56.0":
|
||||
version: 8.56.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.56.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/project-service": "npm:8.56.1"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:8.56.1"
|
||||
"@typescript-eslint/types": "npm:8.56.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.56.1"
|
||||
"@typescript-eslint/project-service": "npm:8.56.0"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:8.56.0"
|
||||
"@typescript-eslint/types": "npm:8.56.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.56.0"
|
||||
debug: "npm:^4.4.3"
|
||||
minimatch: "npm:^10.2.2"
|
||||
minimatch: "npm:^9.0.5"
|
||||
semver: "npm:^7.7.3"
|
||||
tinyglobby: "npm:^0.2.15"
|
||||
ts-api-utils: "npm:^2.4.0"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10/af39dae0a8fada72295a11f0efb49f311241134b0a3d819100eeda6a2f92368844645873ba785de5513ad541cd9c2ba17b9bfed2679daac4682fa2a3b627f087
|
||||
checksum: 10/55c8cfc7e265f320d780e69a677838821225fad8b853108ce2095f01509bf2ee8943280df9ac75560ed86265ef0e0a979ae4cb375d712f648b336032de79d19b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/utils@npm:8.56.1":
|
||||
version: 8.56.1
|
||||
resolution: "@typescript-eslint/utils@npm:8.56.1"
|
||||
"@typescript-eslint/utils@npm:8.56.0":
|
||||
version: 8.56.0
|
||||
resolution: "@typescript-eslint/utils@npm:8.56.0"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": "npm:^4.9.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.56.1"
|
||||
"@typescript-eslint/types": "npm:8.56.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.56.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.56.0"
|
||||
"@typescript-eslint/types": "npm:8.56.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.56.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10/528cbd187d8288a8cfce24a043f993b93711087f53d2b6f95cdd615a1a4087af1dab083a747761af97474a621c7b14f11c84ee50c250f31566ebc64cf73867fe
|
||||
checksum: 10/f357bd15fe568cba0b89371e9a724eda38d78361a21dc0c4f49b0af4a23a140c77e2a8c6285c6fe8d8277e256a8a137aef7bcf6d97428eecd0c6e72ef08849ae
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:8.56.1":
|
||||
version: 8.56.1
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.56.1"
|
||||
"@typescript-eslint/visitor-keys@npm:8.56.0":
|
||||
version: 8.56.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.56.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.56.1"
|
||||
"@typescript-eslint/types": "npm:8.56.0"
|
||||
eslint-visitor-keys: "npm:^5.0.0"
|
||||
checksum: 10/efed6a9867e7be203eec543e5a65da5aaec96aae55fba6fe74a305bf600e57c707764835e82bb8eb541f49a9b70442ff1e1a0ecf3543c78c91b84dda43b95c53
|
||||
checksum: 10/1eaa26ffe8a2c83d42d428beef207d793aef73c2e306f94e716d39519eaaa07547da898c7d63a5c406a3662895d735b4b1f33b513a1addb69473c65e7d92a2b5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -6086,12 +6078,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"barcode-detector@npm:3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "barcode-detector@npm:3.1.0"
|
||||
"barcode-detector@npm:3.0.8":
|
||||
version: 3.0.8
|
||||
resolution: "barcode-detector@npm:3.0.8"
|
||||
dependencies:
|
||||
zxing-wasm: "npm:3.0.0"
|
||||
checksum: 10/60767161081b827e290b60bb3416999dee616bab39291ee55565df9b72d59f0bbbf511fd3bb85db18eee7c0ad9acf1ff90359cdb21e10f80793acd0105c86a1d
|
||||
zxing-wasm: "npm:2.2.4"
|
||||
checksum: 10/7de6225f659c69a0f4101d080a9e0812f2404c485fa2406424b8b13eaff274f6e7405c94de24827a09def52c32a63b19b9c9fba61c5274b074d558f696ec1684
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -9200,7 +9192,7 @@ __metadata:
|
||||
"@fullcalendar/luxon3": "npm:6.1.20"
|
||||
"@fullcalendar/timegrid": "npm:6.1.20"
|
||||
"@home-assistant/webawesome": "npm:3.2.1-ha.3"
|
||||
"@html-eslint/eslint-plugin": "npm:0.57.0"
|
||||
"@html-eslint/eslint-plugin": "npm:0.56.0"
|
||||
"@lezer/highlight": "npm:1.2.3"
|
||||
"@lit-labs/motion": "npm:1.1.0"
|
||||
"@lit-labs/observers": "npm:2.1.0"
|
||||
@@ -9268,7 +9260,7 @@ __metadata:
|
||||
app-datepicker: "npm:5.1.1"
|
||||
babel-loader: "npm:10.0.0"
|
||||
babel-plugin-template-html-minifier: "npm:4.1.0"
|
||||
barcode-detector: "npm:3.1.0"
|
||||
barcode-detector: "npm:3.0.8"
|
||||
browserslist-useragent-regexp: "npm:4.1.3"
|
||||
color-name: "npm:2.1.0"
|
||||
comlink: "npm:4.4.2"
|
||||
@@ -9343,7 +9335,7 @@ __metadata:
|
||||
tinykeys: "npm:3.0.0"
|
||||
ts-lit-plugin: "npm:2.0.2"
|
||||
typescript: "npm:5.9.3"
|
||||
typescript-eslint: "npm:8.56.1"
|
||||
typescript-eslint: "npm:8.56.0"
|
||||
ua-parser-js: "npm:2.0.9"
|
||||
vite-tsconfig-paths: "npm:6.1.1"
|
||||
vitest: "npm:4.0.18"
|
||||
@@ -11125,6 +11117,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimatch@npm:^9.0.5":
|
||||
version: 9.0.5
|
||||
resolution: "minimatch@npm:9.0.5"
|
||||
dependencies:
|
||||
brace-expansion: "npm:^2.0.1"
|
||||
checksum: 10/dd6a8927b063aca6d910b119e1f2df6d2ce7d36eab91de83167dd136bb85e1ebff97b0d3de1cb08bd1f7e018ca170b4962479fefab5b2a69e2ae12cb2edc8348
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimist@npm:^1.2.0, minimist@npm:^1.2.6":
|
||||
version: 1.2.8
|
||||
resolution: "minimist@npm:1.2.8"
|
||||
@@ -14139,12 +14140,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"type-fest@npm:^5.4.4":
|
||||
version: 5.4.4
|
||||
resolution: "type-fest@npm:5.4.4"
|
||||
"type-fest@npm:^5.2.0":
|
||||
version: 5.4.1
|
||||
resolution: "type-fest@npm:5.4.1"
|
||||
dependencies:
|
||||
tagged-tag: "npm:^1.0.0"
|
||||
checksum: 10/0bbdca645f95740587f389a2d712fe8d5e9ab7d13e74aac97cf396112510abcaab6b75fd90d65172bc13b02fdfc827e6a871322cc9c1c1a5a2754d9ab264c6f5
|
||||
checksum: 10/be7d4749e1e5cf2e2c9904fa1aaf9da5eef6c47c130881bf93bfd5a670b2ab59c5502466768e42c521281056a2375b1617176a75cf6c52b575f4bbabbd450b21
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -14211,18 +14212,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript-eslint@npm:8.56.1":
|
||||
version: 8.56.1
|
||||
resolution: "typescript-eslint@npm:8.56.1"
|
||||
"typescript-eslint@npm:8.56.0":
|
||||
version: 8.56.0
|
||||
resolution: "typescript-eslint@npm:8.56.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin": "npm:8.56.1"
|
||||
"@typescript-eslint/parser": "npm:8.56.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.56.1"
|
||||
"@typescript-eslint/utils": "npm:8.56.1"
|
||||
"@typescript-eslint/eslint-plugin": "npm:8.56.0"
|
||||
"@typescript-eslint/parser": "npm:8.56.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.56.0"
|
||||
"@typescript-eslint/utils": "npm:8.56.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10/e18cd347ddce0f0e5b28121346f27736a418adf68e73d613690ea3a1d0adfe03bc393f77a8872c2cef77ca74bcc0974212d1775c360de33a9987a94cda11a05b
|
||||
checksum: 10/7c07af35e6b4eaaebad4b50c37bc6dd50f0cecebe5a4e648ef117fd43a8496f3132020061e19a2fbaf826978e91c100054e638701bf89db8f342dd1353bb5b7e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -15703,14 +15704,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zxing-wasm@npm:3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "zxing-wasm@npm:3.0.0"
|
||||
"zxing-wasm@npm:2.2.4":
|
||||
version: 2.2.4
|
||||
resolution: "zxing-wasm@npm:2.2.4"
|
||||
dependencies:
|
||||
"@types/emscripten": "npm:^1.41.5"
|
||||
type-fest: "npm:^5.4.4"
|
||||
type-fest: "npm:^5.2.0"
|
||||
peerDependencies:
|
||||
"@types/emscripten": ">=1.39.6"
|
||||
checksum: 10/0acf04829acf8f3987173af011784642792fc877c7765f79222fe33efff8af09fbf95bf5d590d2490ae39ec411e6c4de06ea24e96d4eb48189b9d06f7502eaa2
|
||||
checksum: 10/e5928cbb066c854c970cbf724e978e502c3469d69de2469bd37d1f6ab8f5d2a2acdbaa9dea32d35cfc058e2b482e29a9c4f12161d9df3e1e952c30dda3a96d8c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
Reference in New Issue
Block a user