mirror of
https://github.com/home-assistant/frontend.git
synced 2026-02-26 19:37:42 +00:00
Compare commits
11 Commits
20260225.0
...
rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
030a9a492c | ||
|
|
2685a007e7 | ||
|
|
9ca1cfbf4a | ||
|
|
0793af6846 | ||
|
|
bb7f441d8d | ||
|
|
2813ed7938 | ||
|
|
9ebfa4029b | ||
|
|
6190ba18ea | ||
|
|
81feea1109 | ||
|
|
be430931cc | ||
|
|
e07194027a |
@@ -515,6 +515,14 @@ 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>
|
||||
@@ -527,6 +535,34 @@ 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,13 +380,29 @@ 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>--dialog-z-index</code></td>
|
||||
<td>Z-index for the dialog.</td>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--dialog-surface-margin-top</code></td>
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20260225.0"
|
||||
version = "20260226.0"
|
||||
license = "Apache-2.0"
|
||||
license-files = ["LICENSE*"]
|
||||
description = "The Home Assistant frontend"
|
||||
|
||||
@@ -31,9 +31,18 @@ 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,6 +25,27 @@ 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;
|
||||
@@ -141,6 +162,9 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
||||
private _handleKeyDown = (ev: KeyboardEvent) => {
|
||||
if (ev.key === "Escape") {
|
||||
this._escapePressed = true;
|
||||
if (this.preventScrimClose) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
ev.stopPropagation();
|
||||
(ev.currentTarget as WaDrawer).open = false;
|
||||
}
|
||||
@@ -382,6 +406,26 @@ 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%;
|
||||
@@ -396,7 +440,18 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
||||
);
|
||||
background-color: var(
|
||||
--ha-bottom-sheet-surface-background,
|
||||
var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff)),
|
||||
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)
|
||||
);
|
||||
padding: var(
|
||||
--ha-bottom-sheet-padding,
|
||||
|
||||
@@ -93,6 +93,8 @@ export class HaDateRangePicker extends LitElement {
|
||||
| "center"
|
||||
| "inline";
|
||||
|
||||
@state() private _calcedVerticalOpeningDirection?: "up" | "down";
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues) {
|
||||
if (
|
||||
(!this.hasUpdated && this.ranges === undefined) ||
|
||||
@@ -134,7 +136,9 @@ export class HaDateRangePicker extends LitElement {
|
||||
opening-direction=${ifDefined(
|
||||
this.openingDirection || this._calcedOpeningDirection
|
||||
)}
|
||||
opens-vertical=${ifDefined(this.verticalOpeningDirection)}
|
||||
opens-vertical=${ifDefined(
|
||||
this.verticalOpeningDirection || this._calcedVerticalOpeningDirection
|
||||
)}
|
||||
first-day=${firstWeekdayIndex(this.hass.locale)}
|
||||
language=${this.hass.locale.language}
|
||||
@change=${this._handleChange}
|
||||
@@ -328,17 +332,24 @@ export class HaDateRangePicker extends LitElement {
|
||||
|
||||
private _handleClick() {
|
||||
// calculate opening direction if not set
|
||||
if (!this._dateRangePicker.open && !this.openingDirection) {
|
||||
const datePickerPosition = this.getBoundingClientRect().x;
|
||||
let opens: "right" | "left" | "center" | "inline";
|
||||
if (datePickerPosition > (2 * window.innerWidth) / 3) {
|
||||
opens = "left";
|
||||
} else if (datePickerPosition < window.innerWidth / 3) {
|
||||
opens = "right";
|
||||
} else {
|
||||
opens = "center";
|
||||
if (!this._dateRangePicker.open) {
|
||||
if (!this.openingDirection) {
|
||||
const datePickerPosition = this.getBoundingClientRect().x;
|
||||
let opens: "right" | "left" | "center" | "inline";
|
||||
if (datePickerPosition > (2 * window.innerWidth) / 3) {
|
||||
opens = "left";
|
||||
} else if (datePickerPosition < window.innerWidth / 3) {
|
||||
opens = "right";
|
||||
} else {
|
||||
opens = "center";
|
||||
}
|
||||
this._calcedOpeningDirection = opens;
|
||||
}
|
||||
if (!this.verticalOpeningDirection) {
|
||||
const rect = this.getBoundingClientRect();
|
||||
this._calcedVerticalOpeningDirection =
|
||||
rect.top > window.innerHeight / 2 ? "up" : "down";
|
||||
}
|
||||
this._calcedOpeningDirection = opens;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,12 @@ 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.
|
||||
@@ -239,6 +244,9 @@ export class HaDialog extends ScrollableFadeMixin(LitElement) {
|
||||
private _handleKeyDown(ev: KeyboardEvent) {
|
||||
if (ev.key === "Escape") {
|
||||
this._escapePressed = true;
|
||||
if (this.preventScrimClose) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
ev.stopPropagation();
|
||||
(ev.currentTarget as WaDialog).open = false;
|
||||
}
|
||||
@@ -268,10 +276,6 @@ 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))
|
||||
@@ -302,6 +306,12 @@ 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));
|
||||
@@ -331,6 +341,18 @@ 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;
|
||||
|
||||
@@ -37,6 +37,9 @@ export class HaIconButtonToggle extends HaIconButton {
|
||||
background-color: transparent;
|
||||
border: 2px solid var(--primary-text-color);
|
||||
}
|
||||
:host([selected]) ha-button::after {
|
||||
opacity: 0;
|
||||
}
|
||||
:host([selected]) ha-button::part(base) {
|
||||
color: var(--primary-background-color);
|
||||
background-color: unset;
|
||||
|
||||
@@ -288,7 +288,7 @@ export class QuickBar extends LitElement {
|
||||
<ha-combo-box-item
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
style="--mdc-icon-size: 32px;"
|
||||
style="--mdc-icon-size: 24px;"
|
||||
>
|
||||
${"stateObj" in item && item.stateObj
|
||||
? html`
|
||||
@@ -302,6 +302,7 @@ export class QuickBar extends LitElement {
|
||||
? html`
|
||||
<ha-domain-icon
|
||||
slot="start"
|
||||
style="margin: var(--ha-space-1);"
|
||||
.hass=${this.hass}
|
||||
.domain=${item.domain}
|
||||
brand-fallback
|
||||
@@ -319,7 +320,11 @@ export class QuickBar extends LitElement {
|
||||
/>
|
||||
`
|
||||
: item.icon
|
||||
? html`<ha-icon slot="start" .icon=${item.icon}></ha-icon>`
|
||||
? html`<ha-icon
|
||||
style="margin: var(--ha-space-1);"
|
||||
slot="start"
|
||||
.icon=${item.icon}
|
||||
></ha-icon>`
|
||||
: "iconColor" in item && item.iconColor
|
||||
? html`
|
||||
<div
|
||||
@@ -333,7 +338,11 @@ export class QuickBar extends LitElement {
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<ha-svg-icon slot="start" .path=${iconPath}></ha-svg-icon>
|
||||
<ha-svg-icon
|
||||
style="margin: var(--ha-space-1);"
|
||||
slot="start"
|
||||
.path=${iconPath}
|
||||
></ha-svg-icon>
|
||||
`}
|
||||
<span slot="headline">${item.primary}</span>
|
||||
${item.secondary
|
||||
|
||||
@@ -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, haStyleScrollbar } from "../../../resources/styles";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { isMac } from "../../../util/is_mac";
|
||||
@@ -255,90 +255,88 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
</ha-dropdown-item>
|
||||
</ha-dropdown>
|
||||
|
||||
<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
|
||||
<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}
|
||||
.pages=${categoryPages}
|
||||
></ha-config-navigation>
|
||||
</ha-card>
|
||||
`
|
||||
)}
|
||||
<ha-tip .hass=${this.hass}>${this._tip}</ha-tip>
|
||||
</ha-config-section>
|
||||
</div>
|
||||
.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>
|
||||
</ha-top-app-bar-fixed>
|
||||
`;
|
||||
}
|
||||
@@ -394,36 +392,7 @@ 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;
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ import "../../../../../components/ha-md-list-item";
|
||||
import "../../../../../components/ha-svg-icon";
|
||||
import type { ConfigEntry } from "../../../../../data/config_entries";
|
||||
import { getConfigEntries } from "../../../../../data/config_entries";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../../../../layouts/hass-subpage";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
|
||||
const THREAD_ICON =
|
||||
"m 17.126982,8.0730792 c 0,-0.7297242 -0.593746,-1.32357 -1.323637,-1.32357 -0.729454,0 -1.323199,0.5938458 -1.323199,1.32357 v 1.3234242 l 1.323199,1.458e-4 c 0.729891,0 1.323637,-0.5937006 1.323637,-1.32357 z M 11.999709,0 C 5.3829818,0 0,5.3838955 0,12.001455 0,18.574352 5.3105455,23.927406 11.865164,24 V 12.012075 l -3.9275642,-2.91e-4 c -1.1669814,0 -2.1169453,0.949979 -2.1169453,2.118323 0,1.16718 0.9499639,2.116868 2.1169453,2.116868 v 2.615717 c -2.6093089,0 -4.732218,-2.12327 -4.732218,-4.732585 0,-2.61048 2.1229091,-4.7343308 4.732218,-4.7343308 l 3.9275642,5.82e-4 v -1.323279 c 0,-2.172296 1.766691,-3.9395777 3.938181,-3.9395777 2.171928,0 3.9392,1.7672817 3.9392,3.9395777 0,2.1721498 -1.767272,3.9395768 -3.9392,3.9395768 l -1.323199,-1.45e-4 V 23.744102 C 19.911127,22.597726 24,17.768833 24,12.001455 24,5.3838955 18.616727,0 11.999709,0 Z";
|
||||
@@ -312,7 +312,8 @@ export class MatterConfigDashboard extends LitElement {
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: var(--ha-space-2) var(--ha-space-4) var(--ha-space-4);
|
||||
padding: var(--ha-space-2) var(--ha-space-4)
|
||||
calc(var(--ha-space-16) + var(--safe-area-inset-bottom, 0px));
|
||||
}
|
||||
|
||||
a[slot="fab"] {
|
||||
|
||||
@@ -37,10 +37,10 @@ import {
|
||||
} from "../../../../../data/zha";
|
||||
import { showOptionsFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-options-flow";
|
||||
import { showAlertDialog } from "../../../../../dialogs/generic/show-dialog-box";
|
||||
import { fileDownload } from "../../../../../util/file_download";
|
||||
import "../../../../../layouts/hass-subpage";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../../../../types";
|
||||
import { fileDownload } from "../../../../../util/file_download";
|
||||
|
||||
@customElement("zha-config-dashboard")
|
||||
class ZHAConfigDashboard extends LitElement {
|
||||
@@ -520,7 +520,8 @@ class ZHAConfigDashboard extends LitElement {
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: var(--ha-space-2) var(--ha-space-4) var(--ha-space-4);
|
||||
padding: var(--ha-space-2) var(--ha-space-4)
|
||||
calc(var(--ha-space-20) + var(--safe-area-inset-bottom, 0px));
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -18,6 +18,7 @@ import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { goBack } from "../../../../../common/navigate";
|
||||
import "../../../../../components/ha-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-fab";
|
||||
@@ -28,7 +29,6 @@ import "../../../../../components/ha-md-list-item";
|
||||
import "../../../../../components/ha-progress-ring";
|
||||
import "../../../../../components/ha-spinner";
|
||||
import "../../../../../components/ha-svg-icon";
|
||||
import { goBack } from "../../../../../common/navigate";
|
||||
import type { ConfigEntry } from "../../../../../data/config_entries";
|
||||
import {
|
||||
ERROR_STATES,
|
||||
@@ -968,7 +968,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: var(--ha-space-2) var(--ha-space-4) var(--ha-space-4);
|
||||
padding: var(--ha-space-2) var(--ha-space-4)
|
||||
calc(var(--ha-space-16) + var(--safe-area-inset-bottom, 0px));
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -7,11 +7,7 @@ import type { HomeAssistant } from "../../../types";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
|
||||
import { shouldShowFloorsAndAreas } from "./show-floors-and-areas";
|
||||
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
|
||||
import {
|
||||
LARGE_SCREEN_CONDITION,
|
||||
SMALL_SCREEN_CONDITION,
|
||||
} from "../../lovelace/strategies/helpers/screen-conditions";
|
||||
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
||||
import type { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
|
||||
|
||||
@customElement("power-view-strategy")
|
||||
export class PowerViewStrategy extends ReactiveElement {
|
||||
@@ -49,22 +45,15 @@ export class PowerViewStrategy extends ReactiveElement {
|
||||
(source) => source.type === "gas" && source.stat_rate
|
||||
);
|
||||
|
||||
const tileSection: LovelaceSectionConfig = {
|
||||
type: "grid",
|
||||
cards: [],
|
||||
column_span: 2,
|
||||
};
|
||||
const chartsSection: LovelaceSectionConfig = {
|
||||
type: "grid",
|
||||
cards: [],
|
||||
column_span: 2,
|
||||
};
|
||||
const tiles: LovelaceCardConfig[] = [];
|
||||
const badges: LovelaceBadgeConfig[] = [];
|
||||
|
||||
const view: LovelaceViewConfig = {
|
||||
type: "sections",
|
||||
sections: [tileSection, chartsSection],
|
||||
max_columns: 2,
|
||||
sections: [chartsSection],
|
||||
};
|
||||
|
||||
// No sources configured
|
||||
@@ -80,11 +69,10 @@ export class PowerViewStrategy extends ReactiveElement {
|
||||
}
|
||||
|
||||
if (hasPowerSources) {
|
||||
const card = {
|
||||
badges.push({
|
||||
type: "power-total",
|
||||
collection_key: collectionKey,
|
||||
};
|
||||
tiles.push(card);
|
||||
});
|
||||
|
||||
chartsSection.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.power_sources_graph_title"),
|
||||
@@ -97,19 +85,17 @@ export class PowerViewStrategy extends ReactiveElement {
|
||||
}
|
||||
|
||||
if (hasGasSources) {
|
||||
const card = {
|
||||
badges.push({
|
||||
type: "gas-total",
|
||||
collection_key: collectionKey,
|
||||
};
|
||||
tiles.push({ ...card });
|
||||
});
|
||||
}
|
||||
|
||||
if (hasWaterSources) {
|
||||
const card = {
|
||||
badges.push({
|
||||
type: "water-total",
|
||||
collection_key: collectionKey,
|
||||
};
|
||||
tiles.push({ ...card });
|
||||
});
|
||||
}
|
||||
|
||||
if (hasPowerDevices) {
|
||||
@@ -148,21 +134,8 @@ export class PowerViewStrategy extends ReactiveElement {
|
||||
});
|
||||
}
|
||||
|
||||
tiles.forEach((card) => {
|
||||
tileSection.cards!.push({
|
||||
...card,
|
||||
grid_options: { columns: 24 / tiles.length },
|
||||
});
|
||||
});
|
||||
|
||||
if (tiles.length > 2) {
|
||||
// On small screens with 3 tiles, show them in 1 column
|
||||
tileSection.visibility = [LARGE_SCREEN_CONDITION];
|
||||
view.sections!.unshift({
|
||||
type: "grid",
|
||||
cards: tiles,
|
||||
visibility: [SMALL_SCREEN_CONDITION],
|
||||
});
|
||||
if (badges.length) {
|
||||
view.badges = badges;
|
||||
}
|
||||
|
||||
return view;
|
||||
|
||||
@@ -3,11 +3,8 @@ import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-badge";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/tile/ha-tile-container";
|
||||
import "../../../../components/tile/ha-tile-icon";
|
||||
import "../../../../components/tile/ha-tile-info";
|
||||
import type { EnergyData, EnergyPreferences } from "../../../../data/energy";
|
||||
import {
|
||||
formatFlowRateShort,
|
||||
@@ -16,18 +13,17 @@ import {
|
||||
} from "../../../../data/energy";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { LovelaceCard, LovelaceGridOptions } from "../../types";
|
||||
import { tileCardStyle } from "../tile/tile-card-style";
|
||||
import type { GasTotalCardConfig } from "../types";
|
||||
import type { LovelaceBadge } from "../../types";
|
||||
import type { GasTotalBadgeConfig } from "../types";
|
||||
|
||||
@customElement("hui-gas-total-card")
|
||||
export class HuiGasTotalCard
|
||||
@customElement("hui-gas-total-badge")
|
||||
export class HuiGasTotalBadge
|
||||
extends SubscribeMixin(LitElement)
|
||||
implements LovelaceCard
|
||||
implements LovelaceBadge
|
||||
{
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _config?: GasTotalCardConfig;
|
||||
@state() private _config?: GasTotalBadgeConfig;
|
||||
|
||||
@state() private _data?: EnergyData;
|
||||
|
||||
@@ -35,7 +31,7 @@ export class HuiGasTotalCard
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||
|
||||
public setConfig(config: GasTotalCardConfig): void {
|
||||
public setConfig(config: GasTotalBadgeConfig): void {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
@@ -49,34 +45,19 @@ export class HuiGasTotalCard
|
||||
];
|
||||
}
|
||||
|
||||
public getCardSize(): Promise<number> | number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getGridOptions(): LovelaceGridOptions {
|
||||
return {
|
||||
columns: 12,
|
||||
min_columns: 6,
|
||||
rows: 1,
|
||||
min_rows: 1,
|
||||
};
|
||||
}
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
if (changedProps.has("_config") || changedProps.has("_data")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if any of the tracked entity states have changed
|
||||
if (changedProps.has("hass")) {
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
if (!oldHass || !this._entities.size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only update if one of our tracked entities changed
|
||||
for (const entityId of this._entities) {
|
||||
if (oldHass.states[entityId] !== this.hass.states[entityId]) {
|
||||
if (oldHass.states[entityId] !== this.hass?.states[entityId]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -122,32 +103,22 @@ export class HuiGasTotalCard
|
||||
this.hass.localize("ui.panel.lovelace.cards.energy.gas_total_title");
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<ha-tile-container .interactive=${false}>
|
||||
<ha-tile-icon slot="icon" data-domain="sensor" data-state="active">
|
||||
<ha-svg-icon slot="icon" .path=${mdiFire}></ha-svg-icon>
|
||||
</ha-tile-icon>
|
||||
<ha-tile-info slot="info">
|
||||
<span slot="primary" class="primary">${name}</span>
|
||||
<span slot="secondary" class="secondary">${displayValue}</span>
|
||||
</ha-tile-info>
|
||||
</ha-tile-container>
|
||||
</ha-card>
|
||||
<ha-badge .label=${name}>
|
||||
<ha-svg-icon slot="icon" .path=${mdiFire}></ha-svg-icon>
|
||||
${displayValue}
|
||||
</ha-badge>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
tileCardStyle,
|
||||
css`
|
||||
:host {
|
||||
--tile-color: var(--energy-gas-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
static styles = css`
|
||||
ha-badge {
|
||||
--badge-color: var(--energy-gas-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-gas-total-card": HuiGasTotalCard;
|
||||
"hui-gas-total-badge": HuiGasTotalBadge;
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,8 @@ import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { formatNumber } from "../../../../common/number/format_number";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-badge";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/tile/ha-tile-container";
|
||||
import "../../../../components/tile/ha-tile-icon";
|
||||
import "../../../../components/tile/ha-tile-info";
|
||||
import type { EnergyData, EnergyPreferences } from "../../../../data/energy";
|
||||
import {
|
||||
getEnergyDataCollection,
|
||||
@@ -16,18 +13,17 @@ import {
|
||||
} from "../../../../data/energy";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { LovelaceCard, LovelaceGridOptions } from "../../types";
|
||||
import { tileCardStyle } from "../tile/tile-card-style";
|
||||
import type { PowerTotalCardConfig } from "../types";
|
||||
import type { LovelaceBadge } from "../../types";
|
||||
import type { PowerTotalBadgeConfig } from "../types";
|
||||
|
||||
@customElement("hui-power-total-card")
|
||||
export class HuiPowerTotalCard
|
||||
@customElement("hui-power-total-badge")
|
||||
export class HuiPowerTotalBadge
|
||||
extends SubscribeMixin(LitElement)
|
||||
implements LovelaceCard
|
||||
implements LovelaceBadge
|
||||
{
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _config?: PowerTotalCardConfig;
|
||||
@state() private _config?: PowerTotalBadgeConfig;
|
||||
|
||||
@state() private _data?: EnergyData;
|
||||
|
||||
@@ -35,7 +31,7 @@ export class HuiPowerTotalCard
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||
|
||||
public setConfig(config: PowerTotalCardConfig): void {
|
||||
public setConfig(config: PowerTotalBadgeConfig): void {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
@@ -49,34 +45,19 @@ export class HuiPowerTotalCard
|
||||
];
|
||||
}
|
||||
|
||||
public getCardSize(): Promise<number> | number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getGridOptions(): LovelaceGridOptions {
|
||||
return {
|
||||
columns: 12,
|
||||
min_columns: 6,
|
||||
rows: 1,
|
||||
min_rows: 1,
|
||||
};
|
||||
}
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
if (changedProps.has("_config") || changedProps.has("_data")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if any of the tracked entity states have changed
|
||||
if (changedProps.has("hass")) {
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
if (!oldHass || !this._entities.size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only update if one of our tracked entities changed
|
||||
for (const entityId of this._entities) {
|
||||
if (oldHass.states[entityId] !== this.hass.states[entityId]) {
|
||||
if (oldHass.states[entityId] !== this.hass?.states[entityId]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -94,10 +75,10 @@ export class HuiPowerTotalCard
|
||||
this._entities.clear();
|
||||
|
||||
let solar = 0;
|
||||
let from_grid = 0;
|
||||
let to_grid = 0;
|
||||
let from_battery = 0;
|
||||
let to_battery = 0;
|
||||
let fromGrid = 0;
|
||||
let toGrid = 0;
|
||||
let fromBattery = 0;
|
||||
let toBattery = 0;
|
||||
|
||||
prefs.energy_sources.forEach((source) => {
|
||||
if (source.type === "solar" && source.stat_rate) {
|
||||
@@ -105,17 +86,17 @@ export class HuiPowerTotalCard
|
||||
if (value > 0) solar += value;
|
||||
} else if (source.type === "grid" && source.stat_rate) {
|
||||
const value = this._getCurrentPower(source.stat_rate);
|
||||
if (value > 0) from_grid += value;
|
||||
else if (value < 0) to_grid += Math.abs(value);
|
||||
if (value > 0) fromGrid += value;
|
||||
else if (value < 0) toGrid += Math.abs(value);
|
||||
} else if (source.type === "battery" && source.stat_rate) {
|
||||
const value = this._getCurrentPower(source.stat_rate);
|
||||
if (value > 0) from_battery += value;
|
||||
else if (value < 0) to_battery += Math.abs(value);
|
||||
if (value > 0) fromBattery += value;
|
||||
else if (value < 0) toBattery += Math.abs(value);
|
||||
}
|
||||
});
|
||||
|
||||
const used_total = from_grid + solar + from_battery - to_grid - to_battery;
|
||||
return Math.max(0, used_total);
|
||||
const usedTotal = fromGrid + solar + fromBattery - toGrid - toBattery;
|
||||
return Math.max(0, usedTotal);
|
||||
}
|
||||
|
||||
protected render() {
|
||||
@@ -141,35 +122,22 @@ export class HuiPowerTotalCard
|
||||
this.hass.localize("ui.panel.lovelace.cards.energy.power_total_title");
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<ha-tile-container .interactive=${false}>
|
||||
<ha-tile-icon slot="icon" data-domain="sensor" data-state="active">
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiHomeLightningBolt}
|
||||
></ha-svg-icon>
|
||||
</ha-tile-icon>
|
||||
<ha-tile-info slot="info">
|
||||
<span slot="primary" class="primary">${name}</span>
|
||||
<span slot="secondary" class="secondary">${displayValue}</span>
|
||||
</ha-tile-info>
|
||||
</ha-tile-container>
|
||||
</ha-card>
|
||||
<ha-badge .label=${name}>
|
||||
<ha-svg-icon slot="icon" .path=${mdiHomeLightningBolt}></ha-svg-icon>
|
||||
${displayValue}
|
||||
</ha-badge>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
tileCardStyle,
|
||||
css`
|
||||
:host {
|
||||
--tile-color: var(--primary-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
static styles = css`
|
||||
ha-badge {
|
||||
--badge-color: var(--primary-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-power-total-card": HuiPowerTotalCard;
|
||||
"hui-power-total-badge": HuiPowerTotalBadge;
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,8 @@ import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-badge";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/tile/ha-tile-container";
|
||||
import "../../../../components/tile/ha-tile-icon";
|
||||
import "../../../../components/tile/ha-tile-info";
|
||||
import type { EnergyData, EnergyPreferences } from "../../../../data/energy";
|
||||
import {
|
||||
formatFlowRateShort,
|
||||
@@ -16,18 +13,17 @@ import {
|
||||
} from "../../../../data/energy";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { LovelaceCard, LovelaceGridOptions } from "../../types";
|
||||
import { tileCardStyle } from "../tile/tile-card-style";
|
||||
import type { WaterTotalCardConfig } from "../types";
|
||||
import type { LovelaceBadge } from "../../types";
|
||||
import type { WaterTotalBadgeConfig } from "../types";
|
||||
|
||||
@customElement("hui-water-total-card")
|
||||
export class HuiWaterTotalCard
|
||||
@customElement("hui-water-total-badge")
|
||||
export class HuiWaterTotalBadge
|
||||
extends SubscribeMixin(LitElement)
|
||||
implements LovelaceCard
|
||||
implements LovelaceBadge
|
||||
{
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _config?: WaterTotalCardConfig;
|
||||
@state() private _config?: WaterTotalBadgeConfig;
|
||||
|
||||
@state() private _data?: EnergyData;
|
||||
|
||||
@@ -35,7 +31,7 @@ export class HuiWaterTotalCard
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||
|
||||
public setConfig(config: WaterTotalCardConfig): void {
|
||||
public setConfig(config: WaterTotalBadgeConfig): void {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
@@ -49,34 +45,19 @@ export class HuiWaterTotalCard
|
||||
];
|
||||
}
|
||||
|
||||
public getCardSize(): Promise<number> | number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getGridOptions(): LovelaceGridOptions {
|
||||
return {
|
||||
columns: 12,
|
||||
min_columns: 6,
|
||||
rows: 1,
|
||||
min_rows: 1,
|
||||
};
|
||||
}
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
if (changedProps.has("_config") || changedProps.has("_data")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if any of the tracked entity states have changed
|
||||
if (changedProps.has("hass")) {
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
if (!oldHass || !this._entities.size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only update if one of our tracked entities changed
|
||||
for (const entityId of this._entities) {
|
||||
if (oldHass.states[entityId] !== this.hass.states[entityId]) {
|
||||
if (oldHass.states[entityId] !== this.hass?.states[entityId]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -122,32 +103,22 @@ export class HuiWaterTotalCard
|
||||
this.hass.localize("ui.panel.lovelace.cards.energy.water_total_title");
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<ha-tile-container .interactive=${false}>
|
||||
<ha-tile-icon slot="icon" data-domain="sensor" data-state="active">
|
||||
<ha-svg-icon slot="icon" .path=${mdiWater}></ha-svg-icon>
|
||||
</ha-tile-icon>
|
||||
<ha-tile-info slot="info">
|
||||
<span slot="primary" class="primary">${name}</span>
|
||||
<span slot="secondary" class="secondary">${displayValue}</span>
|
||||
</ha-tile-info>
|
||||
</ha-tile-container>
|
||||
</ha-card>
|
||||
<ha-badge .label=${name}>
|
||||
<ha-svg-icon slot="icon" .path=${mdiWater}></ha-svg-icon>
|
||||
${displayValue}
|
||||
</ha-badge>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
tileCardStyle,
|
||||
css`
|
||||
:host {
|
||||
--tile-color: var(--energy-water-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
static styles = css`
|
||||
ha-badge {
|
||||
--badge-color: var(--energy-water-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-water-total-card": HuiWaterTotalCard;
|
||||
"hui-water-total-badge": HuiWaterTotalBadge;
|
||||
}
|
||||
}
|
||||
@@ -48,3 +48,20 @@ export interface EntityBadgeConfig extends LovelaceBadgeConfig {
|
||||
*/
|
||||
display_type?: DisplayType;
|
||||
}
|
||||
|
||||
interface EnergyTotalBadgeConfig extends LovelaceBadgeConfig {
|
||||
title?: string;
|
||||
collection_key?: string;
|
||||
}
|
||||
|
||||
export interface PowerTotalBadgeConfig extends EnergyTotalBadgeConfig {
|
||||
type: "power-total";
|
||||
}
|
||||
|
||||
export interface WaterTotalBadgeConfig extends EnergyTotalBadgeConfig {
|
||||
type: "water-total";
|
||||
}
|
||||
|
||||
export interface GasTotalBadgeConfig extends EnergyTotalBadgeConfig {
|
||||
type: "gas-total";
|
||||
}
|
||||
|
||||
@@ -265,21 +265,6 @@ export interface PowerSourcesGraphCardConfig extends EnergyCardBaseConfig {
|
||||
show_legend?: boolean;
|
||||
}
|
||||
|
||||
export interface PowerTotalCardConfig extends EnergyCardBaseConfig {
|
||||
type: "power-total";
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface WaterTotalCardConfig extends EnergyCardBaseConfig {
|
||||
type: "water-total";
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface GasTotalCardConfig extends EnergyCardBaseConfig {
|
||||
type: "gas-total";
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface PowerSankeyCardConfig extends EnergyCardBaseConfig {
|
||||
type: "power-sankey";
|
||||
title?: string;
|
||||
|
||||
@@ -10,6 +10,9 @@ const ALWAYS_LOADED_TYPES = new Set(["error", "entity"]);
|
||||
const LAZY_LOAD_TYPES = {
|
||||
"entity-filter": () => import("../badges/hui-entity-filter-badge"),
|
||||
"state-label": () => import("../badges/hui-state-label-badge"),
|
||||
"power-total": () => import("../badges/energy/hui-power-total-badge"),
|
||||
"gas-total": () => import("../badges/energy/hui-gas-total-badge"),
|
||||
"water-total": () => import("../badges/energy/hui-water-total-badge"),
|
||||
};
|
||||
|
||||
// This will not return an error card but will throw the error
|
||||
|
||||
@@ -71,9 +71,6 @@ const LAZY_LOAD_TYPES = {
|
||||
import("../cards/water/hui-water-flow-sankey-card"),
|
||||
"power-sources-graph": () =>
|
||||
import("../cards/energy/hui-power-sources-graph-card"),
|
||||
"power-total": () => import("../cards/energy/hui-power-total-card"),
|
||||
"water-total": () => import("../cards/energy/hui-water-total-card"),
|
||||
"gas-total": () => import("../cards/energy/hui-gas-total-card"),
|
||||
"power-sankey": () => import("../cards/energy/hui-power-sankey-card"),
|
||||
"entity-filter": () => import("../cards/hui-entity-filter-card"),
|
||||
error: () => import("../cards/hui-error-card"),
|
||||
|
||||
@@ -631,11 +631,8 @@ class HUIRoot extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleContainerScroll = () => {
|
||||
this.toggleAttribute(
|
||||
"scrolled",
|
||||
this._viewRoot ? this._viewRoot.scrollTop !== 0 : false
|
||||
);
|
||||
private _handleWindowScroll = () => {
|
||||
this.toggleAttribute("scrolled", window.scrollY !== 0);
|
||||
};
|
||||
|
||||
private _locationChanged = () => {
|
||||
@@ -666,7 +663,7 @@ class HUIRoot extends LitElement {
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
this._viewRoot?.addEventListener("scroll", this._handleContainerScroll, {
|
||||
window.addEventListener("scroll", this._handleWindowScroll, {
|
||||
passive: true,
|
||||
});
|
||||
this._handleUrlChanged();
|
||||
@@ -677,7 +674,7 @@ class HUIRoot extends LitElement {
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this._viewRoot?.addEventListener("scroll", this._handleContainerScroll, {
|
||||
window.addEventListener("scroll", this._handleWindowScroll, {
|
||||
passive: true,
|
||||
});
|
||||
window.addEventListener("popstate", this._handlePopState);
|
||||
@@ -688,13 +685,10 @@ class HUIRoot extends LitElement {
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this._viewRoot?.removeEventListener("scroll", this._handleContainerScroll);
|
||||
window.removeEventListener("scroll", this._handleWindowScroll);
|
||||
window.removeEventListener("popstate", this._handlePopState);
|
||||
window.removeEventListener("location-changed", this._locationChanged);
|
||||
this.toggleAttribute(
|
||||
"scrolled",
|
||||
this._viewRoot ? this._viewRoot.scrollTop !== 0 : false
|
||||
);
|
||||
this.toggleAttribute("scrolled", window.scrollY !== 0);
|
||||
// Re-enable history scroll restoration when leaving the page
|
||||
window.history.scrollRestoration = "auto";
|
||||
}
|
||||
@@ -827,11 +821,9 @@ class HUIRoot extends LitElement {
|
||||
(this._restoreScroll && this._viewScrollPositions[newSelectView]) ||
|
||||
0;
|
||||
this._restoreScroll = false;
|
||||
requestAnimationFrame(() => {
|
||||
if (this._viewRoot) {
|
||||
this._viewRoot.scrollTo({ behavior: "auto", top: position });
|
||||
}
|
||||
});
|
||||
requestAnimationFrame(() =>
|
||||
scrollTo({ behavior: "auto", top: position })
|
||||
);
|
||||
}
|
||||
this._selectView(newSelectView, force);
|
||||
});
|
||||
@@ -1156,7 +1148,7 @@ class HUIRoot extends LitElement {
|
||||
const path = this.config.views[viewIndex].path || viewIndex;
|
||||
this._navigateToView(path);
|
||||
} else if (!this._editMode) {
|
||||
this._viewRoot?.scrollTo({ behavior: "smooth", top: 0 });
|
||||
scrollTo({ behavior: "smooth", top: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1167,7 +1159,7 @@ class HUIRoot extends LitElement {
|
||||
|
||||
// Save scroll position of current view
|
||||
if (this._curView != null) {
|
||||
this._viewScrollPositions[this._curView] = this._viewRoot?.scrollTop ?? 0;
|
||||
this._viewScrollPositions[this._curView] = window.scrollY;
|
||||
}
|
||||
|
||||
viewIndex = viewIndex === undefined ? 0 : viewIndex;
|
||||
@@ -1475,14 +1467,9 @@ class HUIRoot extends LitElement {
|
||||
hui-view-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: calc(
|
||||
100vh - var(--header-height) - var(--safe-area-inset-top) - var(
|
||||
--view-container-padding-top,
|
||||
0px
|
||||
)
|
||||
);
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
margin-top: calc(
|
||||
padding-top: calc(
|
||||
var(--header-height) + var(--safe-area-inset-top) +
|
||||
var(--view-container-padding-top, 0px)
|
||||
);
|
||||
@@ -1505,12 +1492,7 @@ class HUIRoot extends LitElement {
|
||||
* In edit mode we have the tab bar on a new line *
|
||||
*/
|
||||
hui-view-container.has-tab-bar {
|
||||
height: calc(
|
||||
100vh - var(--header-height, 56px) - calc(
|
||||
var(--tab-bar-height, 56px) - 2px
|
||||
) - var(--safe-area-inset-top, 0px)
|
||||
);
|
||||
margin-top: calc(
|
||||
padding-top: calc(
|
||||
var(--header-height, 56px) +
|
||||
calc(var(--tab-bar-height, 56px) - 2px) +
|
||||
var(--safe-area-inset-top, 0px)
|
||||
|
||||
@@ -418,15 +418,11 @@ 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 = scrollTop;
|
||||
this._sidebarScrollTop = window.scrollY;
|
||||
} else {
|
||||
this._contentScrollTop = scrollTop;
|
||||
this._contentScrollTop = window.scrollY;
|
||||
}
|
||||
|
||||
this._sidebarTabActive = !this._sidebarTabActive;
|
||||
@@ -442,7 +438,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
||||
const scrollY = this._sidebarTabActive
|
||||
? this._sidebarScrollTop
|
||||
: this._contentScrollTop;
|
||||
scrollContainer?.scrollTo(0, scrollY);
|
||||
window.scrollTo(0, scrollY);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ 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"];
|
||||
@@ -23,7 +22,6 @@ class HuiViewContainer extends LitElement {
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.classList.add("ha-scrollbar");
|
||||
this._setUpMediaQuery();
|
||||
this._applyTheme();
|
||||
}
|
||||
@@ -76,16 +74,11 @@ class HuiViewContainer extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
static styles = [
|
||||
haStyleScrollbar,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
height: 100%;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
`,
|
||||
];
|
||||
static styles = css`
|
||||
:host {
|
||||
display: relative;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -102,6 +102,14 @@ export const getMyRedirects = (): Redirects => ({
|
||||
component: "zwave_js",
|
||||
redirect: "/config/zwave_js/dashboard",
|
||||
},
|
||||
config_matter: {
|
||||
component: "matter",
|
||||
redirect: "/config/matter/dashboard",
|
||||
},
|
||||
config_thread: {
|
||||
component: "thread",
|
||||
redirect: "/config/thread",
|
||||
},
|
||||
add_zigbee_device: {
|
||||
component: "zha",
|
||||
redirect: "/config/zha/add",
|
||||
|
||||
@@ -258,20 +258,17 @@ export const haStyleDialogFixedTop = css`
|
||||
`;
|
||||
|
||||
export const haStyleScrollbar = css`
|
||||
.ha-scrollbar::-webkit-scrollbar,
|
||||
:host(.ha-scrollbar)::-webkit-scrollbar {
|
||||
.ha-scrollbar::-webkit-scrollbar {
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.ha-scrollbar::-webkit-scrollbar-thumb,
|
||||
:host(.ha-scrollbar)::-webkit-scrollbar-thumb {
|
||||
.ha-scrollbar::-webkit-scrollbar-thumb {
|
||||
border-radius: var(--ha-border-radius-sm);
|
||||
background: var(--scrollbar-thumb-color);
|
||||
}
|
||||
|
||||
.ha-scrollbar,
|
||||
:host(.ha-scrollbar) {
|
||||
.ha-scrollbar {
|
||||
overflow-y: auto;
|
||||
scrollbar-color: var(--scrollbar-thumb-color) transparent;
|
||||
scrollbar-width: thin;
|
||||
|
||||
@@ -359,6 +359,8 @@ 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,6 +51,21 @@ 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;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user