Compare commits

..

20 Commits

Author SHA1 Message Date
Paul Bottein
a16f4952ab Simplify scroll lock 2025-10-24 15:44:06 +02:00
Paul Bottein
a4e2070d0f Simplify scroll lock 2025-10-24 15:37:36 +02:00
Wendelin
fef2ff6392 Merge branch 'dev' of github.com:home-assistant/frontend into bottom-sheet-swipe-close 2025-10-24 09:59:47 +02:00
Wendelin
459a0bd178 Merge branch 'add-automation-new-design' of github.com:home-assistant/frontend into bottom-sheet-swipe-close 2025-10-16 16:31:27 +02:00
Wendelin
d808c63c55 Merge branch 'dev' of github.com:home-assistant/frontend into add-automation-new-design 2025-10-16 16:29:10 +02:00
Wendelin
80135af9ec Fix bottom sheet padding 2025-10-16 15:29:27 +02:00
Wendelin
848b737a92 update target picker selector 2025-10-16 15:20:08 +02:00
Wendelin
50d4fae14a Merge branch 'add-automation-new-design' of github.com:home-assistant/frontend into bottom-sheet-swipe-close 2025-10-16 15:16:36 +02:00
Wendelin
1da3570a6f fix translations, scroll issues, RTL 2025-10-16 15:01:57 +02:00
Wendelin
6c4046c842 add swipe to close for bottom sheet 2025-10-16 14:07:41 +02:00
Wendelin
e97b04b5b9 Fix device translation 2025-10-16 10:19:28 +02:00
Wendelin
6dbfb60825 Add keybindings and blocks search separation 2025-10-16 09:44:46 +02:00
Wendelin
ba2881b0bf Add max-height 2025-10-15 17:00:06 +02:00
Wendelin
e42be13682 fix height 2025-10-15 16:53:32 +02:00
Wendelin
bf8dacb7a4 Add tabs 2025-10-15 16:50:18 +02:00
Wendelin
a23fc8fcba revert merge 2025-10-14 17:07:48 +02:00
Wendelin
0ba88246a1 Merge branch 'dev' of github.com:home-assistant/frontend into add-automation-new-design 2025-10-14 17:01:30 +02:00
Wendelin
6529b31b65 WIP new add dialog 2025-10-14 16:59:46 +02:00
Wendelin
866518477f Merge branch 'dev' of github.com:home-assistant/frontend into add-automation-new-design 2025-10-13 13:13:41 +02:00
Wendelin
f831f876de WIP new add automation element 2025-10-10 14:48:18 +02:00
16 changed files with 130 additions and 303 deletions

View File

@@ -1,6 +1,6 @@
import "@home-assistant/webawesome/dist/components/drawer/drawer"; import "@home-assistant/webawesome/dist/components/drawer/drawer";
import { css, html, LitElement, type PropertyValues } from "lit"; import { css, html, LitElement, type PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, query, state } from "lit/decorators";
import { haStyleScrollbar } from "../resources/styles"; import { haStyleScrollbar } from "../resources/styles";
export const BOTTOM_SHEET_ANIMATION_DURATION_MS = 300; export const BOTTOM_SHEET_ANIMATION_DURATION_MS = 300;
@@ -14,6 +14,12 @@ export class HaBottomSheet extends LitElement {
@state() private _drawerOpen = false; @state() private _drawerOpen = false;
@query("#drawer") private _drawer!: HTMLElement;
private _resizeStartY = 0;
private _resizeDelta = 0;
private _handleAfterHide() { private _handleAfterHide() {
this.open = false; this.open = false;
const ev = new Event("closed", { const ev = new Event("closed", {
@@ -33,19 +39,82 @@ export class HaBottomSheet extends LitElement {
render() { render() {
return html` return html`
<wa-drawer <wa-drawer
id="drawer"
placement="bottom" placement="bottom"
.open=${this._drawerOpen} .open=${this._drawerOpen}
@wa-after-hide=${this._handleAfterHide} @wa-after-hide=${this._handleAfterHide}
without-header without-header
@touchstart=${this._handleTouchStart}
> >
<slot name="header"></slot> <slot name="header"></slot>
<div class="body ha-scrollbar"> <div id="body" class="body ha-scrollbar">
<slot></slot> <slot></slot>
</div> </div>
</wa-drawer> </wa-drawer>
`; `;
} }
private _handleTouchStart = (ev: TouchEvent) => {
// Check if any element inside drawer in the composed path has scrollTop > 0
for (const path of ev.composedPath()) {
const el = path as HTMLElement;
if (el === this._drawer) {
break;
}
if (el.scrollTop > 0) {
return;
}
}
this._startResizing(ev.touches[0].clientY);
};
private _startResizing(clientY: number) {
// register event listeners for drag handling
document.addEventListener("touchmove", this._handleMouseMove, {
passive: false,
});
document.addEventListener("touchend", this._endResizing);
document.addEventListener("touchcancel", this._endResizing);
this._resizeStartY = clientY;
}
private _handleMouseMove = (ev: TouchEvent) => {
this._resizeDelta = this._resizeStartY - ev.touches[0].clientY;
if (this._resizeDelta < 0) {
ev.preventDefault();
requestAnimationFrame(() => {
this.style.setProperty(
"--dialog-transform",
`translateY(${this._resizeDelta * -1}px)`
);
});
}
};
private _endResizing = () => {
this._unregisterResizeHandlers();
if (this._resizeDelta > -50) {
this.style.removeProperty("--dialog-transform");
return;
}
this._drawerOpen = false;
};
private _unregisterResizeHandlers = () => {
document.removeEventListener("touchmove", this._handleMouseMove);
document.removeEventListener("touchend", this._unregisterResizeHandlers);
document.removeEventListener("touchcancel", this._unregisterResizeHandlers);
};
disconnectedCallback() {
super.disconnectedCallback();
this._unregisterResizeHandlers();
}
static styles = [ static styles = [
haStyleScrollbar, haStyleScrollbar,
css` css`
@@ -59,6 +128,7 @@ export class HaBottomSheet extends LitElement {
wa-drawer::part(dialog) { wa-drawer::part(dialog) {
max-height: var(--ha-bottom-sheet-max-height, 90vh); max-height: var(--ha-bottom-sheet-max-height, 90vh);
align-items: center; align-items: center;
transform: var(--dialog-transform);
} }
wa-drawer::part(body) { wa-drawer::part(body) {
max-width: var(--ha-bottom-sheet-max-width); max-width: var(--ha-bottom-sheet-max-width);
@@ -90,6 +160,11 @@ export class HaBottomSheet extends LitElement {
max-width: 100%; max-width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: var(
--ha-bottom-sheet-padding,
0 var(--safe-area-inset-right) var(--safe-area-inset-bottom)
var(--safe-area-inset-left)
);
} }
`, `,
]; ];
@@ -99,4 +174,8 @@ declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"ha-bottom-sheet": HaBottomSheet; "ha-bottom-sheet": HaBottomSheet;
} }
interface HASSDomEvents {
"bottom-sheet-lock-resize-changed": boolean;
}
} }

View File

@@ -248,7 +248,7 @@ export class HaFilterDevices extends LitElement {
} }
search-input-outlined { search-input-outlined {
display: block; display: block;
padding: var(--ha-space-1) var(--ha-space-2) 0; padding: 0 8px;
} }
`, `,
]; ];

View File

@@ -199,7 +199,7 @@ export class HaFilterDomains extends LitElement {
} }
search-input-outlined { search-input-outlined {
display: block; display: block;
padding: var(--ha-space-1) var(--ha-space-2) 0; padding: 0 8px;
} }
`, `,
]; ];

View File

@@ -264,7 +264,7 @@ export class HaFilterEntities extends LitElement {
} }
search-input-outlined { search-input-outlined {
display: block; display: block;
padding: var(--ha-space-1) var(--ha-space-2) 0; padding: 0 8px;
} }
`, `,
]; ];

View File

@@ -217,7 +217,7 @@ export class HaFilterIntegrations extends LitElement {
} }
search-input-outlined { search-input-outlined {
display: block; display: block;
padding: var(--ha-space-1) var(--ha-space-2) 0; padding: 0 8px;
} }
`, `,
]; ];

View File

@@ -256,7 +256,7 @@ export class HaFilterLabels extends SubscribeMixin(LitElement) {
} }
search-input-outlined { search-input-outlined {
display: block; display: block;
padding: var(--ha-space-1) var(--ha-space-2) 0; padding: 0 8px;
} }
`, `,
]; ];

View File

@@ -29,7 +29,6 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { toggleAttribute } from "../common/dom/toggle_attribute"; import { toggleAttribute } from "../common/dom/toggle_attribute";
import { stringCompare } from "../common/string/compare"; import { stringCompare } from "../common/string/compare";
import { computeRTL } from "../common/util/compute_rtl";
import { throttle } from "../common/util/throttle"; import { throttle } from "../common/util/throttle";
import { subscribeFrontendUserData } from "../data/frontend"; import { subscribeFrontendUserData } from "../data/frontend";
import type { ActionHandlerDetail } from "../data/lovelace/action_handler"; import type { ActionHandlerDetail } from "../data/lovelace/action_handler";
@@ -537,17 +536,11 @@ class HaSidebar extends SubscribeMixin(LitElement) {
} }
private _renderUserItem(selectedPanel: string) { private _renderUserItem(selectedPanel: string) {
const isRTL = computeRTL(this.hass);
return html` return html`
<ha-md-list-item <ha-md-list-item
href="/profile" href="/profile"
type="link" type="link"
class=${classMap({ class="user ${selectedPanel === "profile" ? " selected" : ""}"
user: true,
selected: selectedPanel === "profile",
rtl: isRTL,
})}
@mouseenter=${this._itemMouseEnter} @mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave} @mouseleave=${this._itemMouseLeave}
> >
@@ -673,7 +666,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
tooltip.style.display = "block"; tooltip.style.display = "block";
tooltip.style.position = "fixed"; tooltip.style.position = "fixed";
tooltip.style.top = `${top}px`; tooltip.style.top = `${top}px`;
tooltip.style.left = `calc(${item.offsetLeft + item.clientWidth + 8}px + var(--safe-area-inset-left, var(--ha-space-0)))`; tooltip.style.left = `calc(${item.offsetLeft + item.clientWidth + 8}px + var(--safe-area-inset-left, 0px))`;
} }
private _hideTooltip() { private _hideTooltip() {
@@ -712,17 +705,13 @@ class HaSidebar extends SubscribeMixin(LitElement) {
background-color: var(--sidebar-background-color); background-color: var(--sidebar-background-color);
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
padding-bottom: calc( padding-bottom: calc(14px + var(--safe-area-inset-bottom, 0px));
14px + var(--safe-area-inset-bottom, var(--ha-space-0))
);
} }
.menu { .menu {
height: calc( height: calc(var(--header-height) + var(--safe-area-inset-top, 0px));
var(--header-height) + var(--safe-area-inset-top, var(--ha-space-0))
);
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
padding: 0 var(--ha-space-1); padding: 0 4px;
border-bottom: 1px solid transparent; border-bottom: 1px solid transparent;
white-space: nowrap; white-space: nowrap;
font-weight: var(--ha-font-weight-normal); font-weight: var(--ha-font-weight-normal);
@@ -737,17 +726,13 @@ class HaSidebar extends SubscribeMixin(LitElement) {
); );
font-size: var(--ha-font-size-xl); font-size: var(--ha-font-size-xl);
align-items: center; align-items: center;
padding-left: calc( padding-left: calc(4px + var(--safe-area-inset-left, 0px));
var(--ha-space-1) + var(--safe-area-inset-left, var(--ha-space-0)) padding-inline-start: calc(4px + var(--safe-area-inset-left, 0px));
);
padding-inline-start: calc(
var(--ha-space-1) + var(--safe-area-inset-left, var(--ha-space-0))
);
padding-inline-end: initial; padding-inline-end: initial;
padding-top: var(--safe-area-inset-top, var(--ha-space-0)); padding-top: var(--safe-area-inset-top, 0px);
} }
:host([expanded]) .menu { :host([expanded]) .menu {
width: calc(256px + var(--safe-area-inset-left, var(--ha-space-0))); width: calc(256px + var(--safe-area-inset-left, 0px));
} }
:host([narrow][expanded]) .menu { :host([narrow][expanded]) .menu {
width: 100%; width: 100%;
@@ -763,8 +748,8 @@ class HaSidebar extends SubscribeMixin(LitElement) {
display: none; display: none;
} }
:host([narrow]) .title { :host([narrow]) .title {
margin: var(--ha-space-0); margin: 0;
padding: var(--ha-space-0) var(--ha-space-4); padding: 0 16px;
} }
:host([expanded]) .title { :host([expanded]) .title {
display: initial; display: initial;
@@ -776,16 +761,13 @@ class HaSidebar extends SubscribeMixin(LitElement) {
ha-fade-in, ha-fade-in,
ha-md-list { ha-md-list {
height: calc( height: calc(
100% - var(--header-height) - var( 100% - var(--header-height) - var(--safe-area-inset-top, 0px) -
--safe-area-inset-top,
var(--ha-space-0)
) -
132px 132px
); );
} }
ha-fade-in { ha-fade-in {
padding: var(--ha-space-1) var(--ha-space-0); padding: 4px 0;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
justify-content: center; justify-content: center;
@@ -795,29 +777,29 @@ class HaSidebar extends SubscribeMixin(LitElement) {
ha-md-list { ha-md-list {
overflow-x: hidden; overflow-x: hidden;
background: none; background: none;
margin-left: var(--safe-area-inset-left, var(--ha-space-0)); margin-left: var(--safe-area-inset-left, 0px);
} }
ha-md-list-item { ha-md-list-item {
flex-shrink: 0; flex-shrink: 0;
box-sizing: border-box; box-sizing: border-box;
margin: var(--ha-space-1); margin: 4px;
border-radius: var(--ha-border-radius-sm); border-radius: var(--ha-border-radius-sm);
--md-list-item-one-line-container-height: var(--ha-space-10); --md-list-item-one-line-container-height: 40px;
--md-list-item-top-space: 0; --md-list-item-top-space: 0;
--md-list-item-bottom-space: 0; --md-list-item-bottom-space: 0;
width: var(--ha-space-12); width: 48px;
position: relative; position: relative;
--md-list-item-label-text-color: var(--sidebar-text-color); --md-list-item-label-text-color: var(--sidebar-text-color);
--md-list-item-leading-space: var(--ha-space-3); --md-list-item-leading-space: 12px;
--md-list-item-trailing-space: var(--ha-space-3); --md-list-item-trailing-space: 12px;
--md-list-item-leading-icon-size: var(--ha-space-6); --md-list-item-leading-icon-size: 24px;
} }
:host([expanded]) ha-md-list-item { :host([expanded]) ha-md-list-item {
width: 248px; width: 248px;
} }
:host([narrow][expanded]) ha-md-list-item { :host([narrow][expanded]) ha-md-list-item {
width: calc(240px - var(--safe-area-inset-left, var(--ha-space-0))); width: calc(240px - var(--safe-area-inset-left, 0px));
} }
ha-md-list-item.selected { ha-md-list-item.selected {
@@ -841,7 +823,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
ha-icon[slot="start"], ha-icon[slot="start"],
ha-svg-icon[slot="start"] { ha-svg-icon[slot="start"] {
width: var(--ha-space-6); width: 24px;
flex-shrink: 0; flex-shrink: 0;
color: var(--sidebar-icon-color); color: var(--sidebar-icon-color);
} }
@@ -874,7 +856,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
min-width: var(--ha-space-2); min-width: 8px;
border-radius: var(--ha-border-radius-xl); border-radius: var(--ha-border-radius-xl);
font-weight: var(--ha-font-weight-normal); font-weight: var(--ha-font-weight-normal);
line-height: normal; line-height: normal;
@@ -885,26 +867,22 @@ class HaSidebar extends SubscribeMixin(LitElement) {
ha-svg-icon + .badge { ha-svg-icon + .badge {
position: absolute; position: absolute;
top: var(--ha-space-1); top: 4px;
left: 26px; left: 26px;
border-radius: var(--ha-border-radius-md); border-radius: var(--ha-border-radius-md);
font-size: 0.65em; font-size: 0.65em;
line-height: var(--ha-line-height-expanded); line-height: var(--ha-line-height-expanded);
padding: var(--ha-space-0) var(--ha-space-1); padding: 0 4px;
} }
ha-md-list-item.user { ha-md-list-item.user {
--md-list-item-leading-icon-size: var(--ha-space-10); --md-list-item-leading-icon-size: 40px;
--md-list-item-leading-space: var(--ha-space-1); --md-list-item-leading-space: 4px;
}
ha-md-list-item.user.rtl {
--md-list-item-leading-space: var(--ha-space-3);
} }
ha-user-badge { ha-user-badge {
flex-shrink: 0; flex-shrink: 0;
margin-right: calc(var(--ha-space-2) * -1); margin-right: -8px;
} }
.spacer { .spacer {
@@ -916,7 +894,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
color: var(--sidebar-text-color); color: var(--sidebar-text-color);
font-size: var(--ha-font-size-m); font-size: var(--ha-font-size-m);
font-weight: var(--ha-font-weight-medium); font-weight: var(--ha-font-weight-medium);
padding: var(--ha-space-4); padding: 16px;
white-space: nowrap; white-space: nowrap;
} }
@@ -928,7 +906,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
white-space: nowrap; white-space: nowrap;
color: var(--sidebar-background-color); color: var(--sidebar-background-color);
background-color: var(--sidebar-text-color); background-color: var(--sidebar-text-color);
padding: var(--ha-space-1); padding: 4px;
font-weight: var(--ha-font-weight-medium); font-weight: var(--ha-font-weight-medium);
} }

View File

@@ -16,7 +16,6 @@ import memoizeOne from "memoize-one";
import { computeCssColor } from "../../common/color/compute-color"; import { computeCssColor } from "../../common/color/compute-color";
import { hex2rgb } from "../../common/color/convert-color"; import { hex2rgb } from "../../common/color/convert-color";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { slugify } from "../../common/string/slugify";
import { import {
computeDeviceName, computeDeviceName,
computeDeviceNameDisplay, computeDeviceNameDisplay,
@@ -103,7 +102,7 @@ export class HaTargetPickerValueChip extends LitElement {
${this.type === "entity" ${this.type === "entity"
? nothing ? nothing
: html`<span role="gridcell"> : html`<span role="gridcell">
<ha-tooltip .for="expand-${slugify(this.itemId)}" <ha-tooltip .for="expand-${this.itemId}"
>${this.hass.localize( >${this.hass.localize(
`ui.components.target-picker.expand_${this.type}_id` `ui.components.target-picker.expand_${this.type}_id`
)} )}
@@ -115,13 +114,13 @@ export class HaTargetPickerValueChip extends LitElement {
)} )}
.path=${mdiUnfoldMoreVertical} .path=${mdiUnfoldMoreVertical}
hide-title hide-title
.id="expand-${slugify(this.itemId)}" .id="expand-${this.itemId}"
.type=${this.type} .type=${this.type}
@click=${this._handleExpand} @click=${this._handleExpand}
></ha-icon-button> ></ha-icon-button>
</span>`} </span>`}
<span role="gridcell"> <span role="gridcell">
<ha-tooltip .for="remove-${slugify(this.itemId)}"> <ha-tooltip .for="remove-${this.itemId}">
${this.hass.localize( ${this.hass.localize(
`ui.components.target-picker.remove_${this.type}_id` `ui.components.target-picker.remove_${this.type}_id`
)} )}
@@ -131,7 +130,7 @@ export class HaTargetPickerValueChip extends LitElement {
.label=${this.hass.localize("ui.components.target-picker.remove")} .label=${this.hass.localize("ui.components.target-picker.remove")}
.path=${mdiClose} .path=${mdiClose}
hide-title hide-title
.id="remove-${slugify(this.itemId)}" .id="remove-${this.itemId}"
.type=${this.type} .type=${this.type}
@click=${this._removeItem} @click=${this._removeItem}
></ha-icon-button> ></ha-icon-button>

View File

@@ -846,8 +846,7 @@ class DialogAddAutomationElement
items: true, items: true,
blank: blank:
!this._selectedGroup && !this._filter && this._tab === "groups", !this._selectedGroup && !this._filter && this._tab === "groups",
"empty-search": "empty-search": !items?.length && this._filter,
!items?.length && !filteredBlockItems?.length && this._filter,
hidden: hidden:
this._narrow && this._narrow &&
!this._selectedGroup && !this._selectedGroup &&
@@ -856,7 +855,7 @@ class DialogAddAutomationElement
})} })}
@scroll=${this._onItemsScroll} @scroll=${this._onItemsScroll}
> >
${filteredBlockItems ${filteredBlockItems && filteredBlockItems.length
? this._renderItemList( ? this._renderItemList(
this.hass.localize(`ui.panel.config.automation.editor.blocks`), this.hass.localize(`ui.panel.config.automation.editor.blocks`),
filteredBlockItems filteredBlockItems
@@ -889,7 +888,7 @@ class DialogAddAutomationElement
} }
private _renderItemList(title, items?: ListItem[]) { private _renderItemList(title, items?: ListItem[]) {
if (!items || !items.length) { if (!items) {
return nothing; return nothing;
} }
@@ -1118,6 +1117,11 @@ class DialogAddAutomationElement
padding: 0; padding: 0;
} }
.items ha-md-list,
.groups {
padding-bottom: max(var(--safe-area-inset-bottom), var(--ha-space-3));
}
.groups { .groups {
overflow: auto; overflow: auto;
flex: 3; flex: 3;
@@ -1202,11 +1206,6 @@ class DialogAddAutomationElement
border: 1px solid var(--ha-color-border-neutral-quiet); border: 1px solid var(--ha-color-border-neutral-quiet);
} }
.items ha-md-list,
.groups {
padding-bottom: max(var(--safe-area-inset-bottom), var(--ha-space-3));
}
.items.blank { .items.blank {
justify-content: center; justify-content: center;
} }

View File

@@ -115,8 +115,8 @@ export class HaConfigLabels extends LitElement {
borderRadius: "var(--ha-border-radius-md)", borderRadius: "var(--ha-border-radius-md)",
border: "1px solid var(--outline-color)", border: "1px solid var(--outline-color)",
boxSizing: "border-box", boxSizing: "border-box",
width: "var(--ha-space-5)", width: "20px",
height: "var(--ha-space-5)", height: "20px",
})} })}
></div>` ></div>`
: nothing, : nothing,

View File

@@ -1,126 +0,0 @@
import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import { supportsFeature } from "../../../common/entity/supports-feature";
import "../../../components/ha-control-number-buttons";
import { isUnavailableState } from "../../../data/entity";
import {
MediaPlayerEntityFeature,
type MediaPlayerEntity,
} from "../../../data/media-player";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { cardFeatureStyles } from "./common/card-feature-styles";
import type {
LovelaceCardFeatureContext,
MediaPlayerVolumeButtonsCardFeatureConfig,
} from "./types";
import { clamp } from "../../../common/number/clamp";
export const supportsMediaPlayerVolumeButtonsCardFeature = (
hass: HomeAssistant,
context: LovelaceCardFeatureContext
) => {
const stateObj = context.entity_id
? hass.states[context.entity_id]
: undefined;
if (!stateObj) return false;
const domain = computeDomain(stateObj.entity_id);
return (
domain === "media_player" &&
supportsFeature(stateObj, MediaPlayerEntityFeature.VOLUME_SET)
);
};
@customElement("hui-media-player-volume-buttons-card-feature")
class HuiMediaPlayerVolumeButtonsCardFeature
extends LitElement
implements LovelaceCardFeature
{
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
@state() private _config?: MediaPlayerVolumeButtonsCardFeatureConfig;
private get _stateObj() {
if (!this.hass || !this.context || !this.context.entity_id) {
return undefined;
}
return this.hass.states[this.context.entity_id] as
| MediaPlayerEntity
| undefined;
}
static getStubConfig(): MediaPlayerVolumeButtonsCardFeatureConfig {
return {
type: "media-player-volume-buttons",
step: 5,
};
}
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
await import(
"../editor/config-elements/hui-media-player-volume-buttons-card-feature-editor"
);
return document.createElement(
"hui-media-player-volume-buttons-card-feature-editor"
);
}
public setConfig(config: MediaPlayerVolumeButtonsCardFeatureConfig): void {
if (!config) {
throw new Error("Invalid configuration");
}
this._config = config;
}
protected render() {
if (
!this._config ||
!this.hass ||
!this.context ||
!this._stateObj ||
!supportsMediaPlayerVolumeButtonsCardFeature(this.hass, this.context)
) {
return nothing;
}
const position =
this._stateObj.attributes.volume_level != null
? Math.round(this._stateObj.attributes.volume_level * 100)
: undefined;
return html`
<ha-control-number-buttons
.disabled=${!this._stateObj || isUnavailableState(this._stateObj.state)}
.locale=${this.hass.locale}
min="0"
max="100"
.step=${this._config.step ?? 5}
.value=${position}
unit="%"
@value-changed=${this._valueChanged}
></ha-control-number-buttons>
`;
}
private _valueChanged(ev: CustomEvent) {
ev.stopPropagation();
this.hass!.callService("media_player", "volume_set", {
entity_id: this._stateObj!.entity_id,
volume_level: clamp(ev.detail.value, 0, 100) / 100,
});
}
static get styles() {
return cardFeatureStyles;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-media-player-volume-buttons-card-feature": HuiMediaPlayerVolumeButtonsCardFeature;
}
}

View File

@@ -50,11 +50,6 @@ export interface MediaPlayerVolumeSliderCardFeatureConfig {
type: "media-player-volume-slider"; type: "media-player-volume-slider";
} }
export interface MediaPlayerVolumeButtonsCardFeatureConfig {
type: "media-player-volume-buttons";
step?: number;
}
export interface FanDirectionCardFeatureConfig { export interface FanDirectionCardFeatureConfig {
type: "fan-direction"; type: "fan-direction";
} }
@@ -257,7 +252,6 @@ export type LovelaceCardFeatureConfig =
| LockCommandsCardFeatureConfig | LockCommandsCardFeatureConfig
| LockOpenDoorCardFeatureConfig | LockOpenDoorCardFeatureConfig
| MediaPlayerPlaybackCardFeatureConfig | MediaPlayerPlaybackCardFeatureConfig
| MediaPlayerVolumeButtonsCardFeatureConfig
| MediaPlayerVolumeSliderCardFeatureConfig | MediaPlayerVolumeSliderCardFeatureConfig
| NumericInputCardFeatureConfig | NumericInputCardFeatureConfig
| SelectOptionsCardFeatureConfig | SelectOptionsCardFeatureConfig

View File

@@ -23,7 +23,6 @@ import "../card-features/hui-light-color-temp-card-feature";
import "../card-features/hui-lock-commands-card-feature"; import "../card-features/hui-lock-commands-card-feature";
import "../card-features/hui-lock-open-door-card-feature"; import "../card-features/hui-lock-open-door-card-feature";
import "../card-features/hui-media-player-playback-card-feature"; import "../card-features/hui-media-player-playback-card-feature";
import "../card-features/hui-media-player-volume-buttons-card-feature";
import "../card-features/hui-media-player-volume-slider-card-feature"; import "../card-features/hui-media-player-volume-slider-card-feature";
import "../card-features/hui-numeric-input-card-feature"; import "../card-features/hui-numeric-input-card-feature";
import "../card-features/hui-select-options-card-feature"; import "../card-features/hui-select-options-card-feature";
@@ -73,7 +72,6 @@ const TYPES = new Set<LovelaceCardFeatureConfig["type"]>([
"lock-commands", "lock-commands",
"lock-open-door", "lock-open-door",
"media-player-playback", "media-player-playback",
"media-player-volume-buttons",
"media-player-volume-slider", "media-player-volume-slider",
"numeric-input", "numeric-input",
"select-options", "select-options",

View File

@@ -48,7 +48,6 @@ import { supportsLightColorTempCardFeature } from "../../card-features/hui-light
import { supportsLockCommandsCardFeature } from "../../card-features/hui-lock-commands-card-feature"; import { supportsLockCommandsCardFeature } from "../../card-features/hui-lock-commands-card-feature";
import { supportsLockOpenDoorCardFeature } from "../../card-features/hui-lock-open-door-card-feature"; import { supportsLockOpenDoorCardFeature } from "../../card-features/hui-lock-open-door-card-feature";
import { supportsMediaPlayerPlaybackCardFeature } from "../../card-features/hui-media-player-playback-card-feature"; import { supportsMediaPlayerPlaybackCardFeature } from "../../card-features/hui-media-player-playback-card-feature";
import { supportsMediaPlayerVolumeButtonsCardFeature } from "../../card-features/hui-media-player-volume-buttons-card-feature";
import { supportsMediaPlayerVolumeSliderCardFeature } from "../../card-features/hui-media-player-volume-slider-card-feature"; import { supportsMediaPlayerVolumeSliderCardFeature } from "../../card-features/hui-media-player-volume-slider-card-feature";
import { supportsNumericInputCardFeature } from "../../card-features/hui-numeric-input-card-feature"; import { supportsNumericInputCardFeature } from "../../card-features/hui-numeric-input-card-feature";
import { supportsSelectOptionsCardFeature } from "../../card-features/hui-select-options-card-feature"; import { supportsSelectOptionsCardFeature } from "../../card-features/hui-select-options-card-feature";
@@ -103,7 +102,6 @@ const UI_FEATURE_TYPES = [
"lock-commands", "lock-commands",
"lock-open-door", "lock-open-door",
"media-player-playback", "media-player-playback",
"media-player-volume-buttons",
"media-player-volume-slider", "media-player-volume-slider",
"numeric-input", "numeric-input",
"select-options", "select-options",
@@ -133,7 +131,6 @@ const EDITABLES_FEATURE_TYPES = new Set<UiFeatureTypes>([
"fan-preset-modes", "fan-preset-modes",
"humidifier-modes", "humidifier-modes",
"lawn-mower-commands", "lawn-mower-commands",
"media-player-volume-buttons",
"numeric-input", "numeric-input",
"select-options", "select-options",
"trend-graph", "trend-graph",
@@ -174,7 +171,6 @@ const SUPPORTS_FEATURE_TYPES: Record<
"lock-commands": supportsLockCommandsCardFeature, "lock-commands": supportsLockCommandsCardFeature,
"lock-open-door": supportsLockOpenDoorCardFeature, "lock-open-door": supportsLockOpenDoorCardFeature,
"media-player-playback": supportsMediaPlayerPlaybackCardFeature, "media-player-playback": supportsMediaPlayerPlaybackCardFeature,
"media-player-volume-buttons": supportsMediaPlayerVolumeButtonsCardFeature,
"media-player-volume-slider": supportsMediaPlayerVolumeSliderCardFeature, "media-player-volume-slider": supportsMediaPlayerVolumeSliderCardFeature,
"numeric-input": supportsNumericInputCardFeature, "numeric-input": supportsNumericInputCardFeature,
"select-options": supportsSelectOptionsCardFeature, "select-options": supportsSelectOptionsCardFeature,

View File

@@ -1,86 +0,0 @@
import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-form/ha-form";
import type { SchemaUnion } from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types";
import type {
LovelaceCardFeatureContext,
MediaPlayerVolumeButtonsCardFeatureConfig,
} from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
@customElement("hui-media-player-volume-buttons-card-feature-editor")
export class HuiMediaPlayerVolumeButtonsCardFeatureEditor
extends LitElement
implements LovelaceCardFeatureEditor
{
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
@state() private _config?: MediaPlayerVolumeButtonsCardFeatureConfig;
public setConfig(config: MediaPlayerVolumeButtonsCardFeatureConfig): void {
this._config = config;
}
private _schema = memoizeOne(
() =>
[
{
name: "step",
selector: {
number: {
mode: "slider",
step: 1,
min: 1,
max: 100,
unit_of_measurement: "%",
},
},
},
] as const
);
protected render() {
if (!this.hass || !this._config) {
return nothing;
}
const data: MediaPlayerVolumeButtonsCardFeatureConfig = {
type: "media-player-volume-buttons",
step: this._config.step ?? 5,
};
const schema = this._schema();
return html`
<ha-form
.hass=${this.hass}
.data=${data}
.schema=${schema}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
></ha-form>
`;
}
private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
}
private _computeLabelCallback = (
schema: SchemaUnion<ReturnType<typeof this._schema>>
) =>
this.hass!.localize(
`ui.panel.lovelace.editor.features.types.media-player-volume-buttons.${schema.name}`
);
}
declare global {
interface HTMLElementTagNameMap {
"hui-media-player-volume-buttons-card-feature-editor": HuiMediaPlayerVolumeButtonsCardFeatureEditor;
}
}

View File

@@ -8151,10 +8151,6 @@
"media-player-playback": { "media-player-playback": {
"label": "Media player playback controls" "label": "Media player playback controls"
}, },
"media-player-volume-buttons": {
"label": "Media player volume buttons",
"step": "Step size"
},
"media-player-volume-slider": { "media-player-volume-slider": {
"label": "Media player volume slider" "label": "Media player volume slider"
}, },