diff --git a/src/common/util/media-progress.ts b/src/common/util/media-progress.ts
new file mode 100644
index 0000000000..c44428c0b8
--- /dev/null
+++ b/src/common/util/media-progress.ts
@@ -0,0 +1,19 @@
+export const startMediaProgressInterval = (
+ interval: number | undefined,
+ callback: () => void,
+ intervalMs = 1000
+): number => {
+ if (interval) {
+ return interval;
+ }
+ return window.setInterval(callback, intervalMs);
+};
+
+export const stopMediaProgressInterval = (
+ interval: number | undefined
+): number | undefined => {
+ if (interval) {
+ clearInterval(interval);
+ }
+ return undefined;
+};
diff --git a/src/common/util/volume-slider.ts b/src/common/util/volume-slider.ts
new file mode 100644
index 0000000000..84921cbd9c
--- /dev/null
+++ b/src/common/util/volume-slider.ts
@@ -0,0 +1,186 @@
+import type { HaSlider } from "../../components/ha-slider";
+
+interface VolumeSliderControllerOptions {
+ getSlider: () => HaSlider | undefined;
+ step: number;
+ onSetVolume: (value: number) => void;
+ onSetVolumeDebounced?: (value: number) => void;
+ onValueUpdated?: (value: number) => void;
+}
+
+export class VolumeSliderController {
+ private _touchStartX = 0;
+
+ private _touchStartY = 0;
+
+ private _touchStartValue = 0;
+
+ private _touchDragging = false;
+
+ private _touchScrolling = false;
+
+ private _dragging = false;
+
+ private _lastValue = 0;
+
+ private _options: VolumeSliderControllerOptions;
+
+ constructor(options: VolumeSliderControllerOptions) {
+ this._options = options;
+ }
+
+ public get isInteracting(): boolean {
+ return this._touchDragging || this._dragging;
+ }
+
+ public setStep(step: number): void {
+ this._options.step = step;
+ }
+
+ public handleInput = (ev: Event): void => {
+ ev.stopPropagation();
+ const value = Number((ev.target as HaSlider).value);
+ this._dragging = true;
+ this._updateValue(value);
+ this._options.onSetVolumeDebounced?.(value);
+ };
+
+ public handleChange = (ev: Event): void => {
+ ev.stopPropagation();
+ const value = Number((ev.target as HaSlider).value);
+ this._dragging = false;
+ this._updateValue(value);
+ this._options.onSetVolume(value);
+ };
+
+ public handleTouchStart = (ev: TouchEvent): void => {
+ ev.stopPropagation();
+ const touch = ev.touches[0];
+ this._touchStartX = touch.clientX;
+ this._touchStartY = touch.clientY;
+ this._touchStartValue = this._getSliderValue();
+ this._touchDragging = false;
+ this._touchScrolling = false;
+ this._showTooltip();
+ };
+
+ public handleTouchMove = (ev: TouchEvent): void => {
+ if (this._touchScrolling) {
+ return;
+ }
+ const touch = ev.touches[0];
+ const deltaX = touch.clientX - this._touchStartX;
+ const deltaY = touch.clientY - this._touchStartY;
+ const absDeltaX = Math.abs(deltaX);
+ const absDeltaY = Math.abs(deltaY);
+
+ if (!this._touchDragging) {
+ if (absDeltaY > 10 && absDeltaY > absDeltaX * 2) {
+ this._touchScrolling = true;
+ return;
+ }
+ if (absDeltaX > 8) {
+ this._touchDragging = true;
+ }
+ }
+
+ if (this._touchDragging) {
+ ev.preventDefault();
+ const newValue = this._getVolumeFromTouch(touch.clientX);
+ this._updateValue(newValue);
+ }
+ };
+
+ public handleTouchEnd = (ev: TouchEvent): void => {
+ if (this._touchScrolling) {
+ this._touchScrolling = false;
+ this._hideTooltip();
+ return;
+ }
+
+ const touch = ev.changedTouches[0];
+ if (!this._touchDragging) {
+ const tapValue = this._getVolumeFromTouch(touch.clientX);
+ const delta =
+ tapValue > this._touchStartValue
+ ? this._options.step
+ : -this._options.step;
+ const newValue = this._roundVolumeValue(this._touchStartValue + delta);
+ this._updateValue(newValue);
+ this._options.onSetVolume(newValue);
+ } else {
+ const finalValue = this._getVolumeFromTouch(touch.clientX);
+ this._updateValue(finalValue);
+ this._options.onSetVolume(finalValue);
+ }
+
+ this._touchDragging = false;
+ this._dragging = false;
+ this._hideTooltip();
+ };
+
+ public handleTouchCancel = (): void => {
+ this._touchDragging = false;
+ this._touchScrolling = false;
+ this._dragging = false;
+ this._updateValue(this._touchStartValue);
+ this._hideTooltip();
+ };
+
+ public handleWheel = (ev: WheelEvent): void => {
+ ev.preventDefault();
+ ev.stopPropagation();
+ const direction = ev.deltaY > 0 ? -1 : 1;
+ const currentValue = this._getSliderValue();
+ const newValue = this._roundVolumeValue(
+ currentValue + direction * this._options.step
+ );
+ this._updateValue(newValue);
+ this._options.onSetVolume(newValue);
+ };
+
+ private _getVolumeFromTouch(clientX: number): number {
+ const slider = this._options.getSlider();
+ if (!slider) {
+ return 0;
+ }
+ const rect = slider.getBoundingClientRect();
+ const x = Math.min(Math.max(clientX - rect.left, 0), rect.width);
+ const percentage = (x / rect.width) * 100;
+ return this._roundVolumeValue(percentage);
+ }
+
+ private _roundVolumeValue(value: number): number {
+ return Math.min(
+ Math.max(Math.round(value / this._options.step) * this._options.step, 0),
+ 100
+ );
+ }
+
+ private _getSliderValue(): number {
+ const slider = this._options.getSlider();
+ if (slider) {
+ return Number(slider.value);
+ }
+ return this._lastValue;
+ }
+
+ private _updateValue(value: number): void {
+ this._lastValue = value;
+ this._options.onValueUpdated?.(value);
+ const slider = this._options.getSlider();
+ if (slider) {
+ slider.value = value;
+ }
+ }
+
+ private _showTooltip(): void {
+ const slider = this._options.getSlider() as any;
+ slider?.showTooltip?.();
+ }
+
+ private _hideTooltip(): void {
+ const slider = this._options.getSlider() as any;
+ slider?.hideTooltip?.();
+ }
+}
diff --git a/src/data/media-player.ts b/src/data/media-player.ts
index 50d414d8b5..4075301d41 100644
--- a/src/data/media-player.ts
+++ b/src/data/media-player.ts
@@ -423,12 +423,17 @@ export const formatMediaTime = (seconds: number | undefined): string => {
return "";
}
- let secondsString = new Date(seconds * 1000).toISOString();
- secondsString =
- seconds > 3600
- ? secondsString.substring(11, 16)
- : secondsString.substring(14, 19);
- return secondsString.replace(/^0+/, "").padStart(4, "0");
+ const totalSeconds = Math.max(0, Math.floor(seconds));
+ const hours = Math.floor(totalSeconds / 3600);
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
+ const secs = totalSeconds % 60;
+ const pad = (value: number) => value.toString().padStart(2, "0");
+
+ if (hours > 0) {
+ return `${pad(hours)}:${pad(minutes)}:${pad(secs)}`;
+ }
+
+ return `${pad(minutes)}:${pad(secs)}`;
};
export const cleanupMediaTitle = (title?: string): string | undefined => {
diff --git a/src/dialogs/more-info/controls/more-info-media_player.ts b/src/dialogs/more-info/controls/more-info-media_player.ts
index e181fb9608..d79b058701 100644
--- a/src/dialogs/more-info/controls/more-info-media_player.ts
+++ b/src/dialogs/more-info/controls/more-info-media_player.ts
@@ -14,9 +14,14 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
-import { formatDurationDigital } from "../../../common/datetime/format_duration";
import { stateActive } from "../../../common/entity/state_active";
import { supportsFeature } from "../../../common/entity/supports-feature";
+import { debounce } from "../../../common/util/debounce";
+import {
+ startMediaProgressInterval,
+ stopMediaProgressInterval,
+} from "../../../common/util/media-progress";
+import { VolumeSliderController } from "../../../common/util/volume-slider";
import "../../../components/chips/ha-assist-chip";
import "../../../components/ha-button";
import "../../../components/ha-icon-button";
@@ -38,6 +43,7 @@ import {
cleanupMediaTitle,
computeMediaControls,
computeMediaDescription,
+ formatMediaTime,
handleMediaControlClick,
MediaPlayerEntityFeature,
mediaPlayerPlayMedia,
@@ -54,6 +60,34 @@ class MoreInfoMediaPlayer extends LitElement {
@query("#position-slider")
private _positionSlider?: HaSlider;
+ @query(".volume-slider")
+ private _volumeSlider?: HaSlider;
+
+ private _progressInterval?: number;
+
+ private _volumeStep = 2;
+
+ private _debouncedVolumeSet = debounce((value: number) => {
+ this._setVolume(value);
+ }, 100);
+
+ private _volumeController = new VolumeSliderController({
+ getSlider: () => this._volumeSlider,
+ step: this._volumeStep,
+ onSetVolume: (value) => this._setVolume(value),
+ onSetVolumeDebounced: (value) => this._debouncedVolumeSet(value),
+ });
+
+ public connectedCallback(): void {
+ super.connectedCallback();
+ this._syncProgressInterval();
+ }
+
+ public disconnectedCallback(): void {
+ super.disconnectedCallback();
+ this._clearProgressInterval();
+ }
+
protected firstUpdated(_changedProperties: PropertyValues) {
if (this._positionSlider) {
this._positionSlider.valueFormatter = (value: number) =>
@@ -62,14 +96,7 @@ class MoreInfoMediaPlayer extends LitElement {
}
private _formatDuration(duration: number) {
- const hours = Math.floor(duration / 3600);
- const minutes = Math.floor((duration % 3600) / 60);
- const seconds = Math.floor(duration % 60);
- return formatDurationDigital(this.hass.locale, {
- hours,
- minutes,
- seconds,
- })!;
+ return formatMediaTime(duration);
}
protected _renderVolumeControl() {
@@ -139,13 +166,25 @@ class MoreInfoMediaPlayer extends LitElement {
${!supportsMute
? html``
: nothing}
-
+
+
+
`
: nothing}
@@ -261,17 +300,17 @@ class MoreInfoMediaPlayer extends LitElement {
const stateObj = this.stateObj;
const controls = computeMediaControls(stateObj, true);
- const coverUrl =
+ const coverUrlRaw =
stateObj.attributes.entity_picture_local ||
stateObj.attributes.entity_picture ||
"";
+ const coverUrl = coverUrlRaw ? this.hass.hassUrl(coverUrlRaw) : "";
const playerObj = new HassMediaPlayerEntity(this.hass, this.stateObj);
const position = Math.max(Math.floor(playerObj.currentProgress || 0), 0);
const duration = Math.max(stateObj.attributes.media_duration || 0, 0);
- const remaining = Math.max(duration - position, 0);
- const remainingFormatted = this._formatDuration(remaining);
const positionFormatted = this._formatDuration(position);
+ const durationFormatted = this._formatDuration(duration);
const primaryTitle = cleanupMediaTitle(stateObj.attributes.media_title);
const secondaryTitle = computeMediaDescription(stateObj);
const turnOn = controls?.find((c) => c.action === "turn_on");
@@ -331,8 +370,12 @@ class MoreInfoMediaPlayer extends LitElement {
?disabled=${!stateActive(stateObj) ||
!supportsFeature(stateObj, MediaPlayerEntityFeature.SEEK)}
>
- ${positionFormatted}
- ${remainingFormatted}
+ ${positionFormatted}
+ ${durationFormatted}
`
@@ -531,6 +574,16 @@ class MoreInfoMediaPlayer extends LitElement {
margin-left: var(--ha-space-2);
}
+ .volume-slider-container {
+ width: 100%;
+ }
+
+ @media (pointer: coarse) {
+ .volume-slider {
+ pointer-events: none;
+ }
+ }
+
.volume ha-svg-icon {
padding: var(--ha-space-1);
height: 16px;
@@ -568,6 +621,10 @@ class MoreInfoMediaPlayer extends LitElement {
color: var(--secondary-text-color);
}
+ .position-time {
+ margin-top: var(--ha-space-2);
+ }
+
.media-info-row {
display: flex;
flex-direction: column;
@@ -622,6 +679,39 @@ class MoreInfoMediaPlayer extends LitElement {
);
}
+ protected updated(changedProps: PropertyValues): void {
+ super.updated(changedProps);
+ if (changedProps.has("stateObj")) {
+ this._syncProgressInterval();
+ }
+ }
+
+ private _syncProgressInterval(): void {
+ if (this._shouldUpdateProgress()) {
+ this._progressInterval = startMediaProgressInterval(
+ this._progressInterval,
+ () => this.requestUpdate()
+ );
+ return;
+ }
+ this._clearProgressInterval();
+ }
+
+ private _clearProgressInterval(): void {
+ this._progressInterval = stopMediaProgressInterval(this._progressInterval);
+ }
+
+ private _shouldUpdateProgress(): boolean {
+ const stateObj = this.stateObj;
+ return (
+ !!stateObj &&
+ stateObj.state === "playing" &&
+ Number(stateObj.attributes.media_duration) > 0 &&
+ "media_position" in stateObj.attributes &&
+ "media_position_updated_at" in stateObj.attributes
+ );
+ }
+
private _toggleMute() {
this.hass!.callService("media_player", "volume_mute", {
entity_id: this.stateObj!.entity_id,
@@ -629,10 +719,10 @@ class MoreInfoMediaPlayer extends LitElement {
});
}
- private _selectedValueChanged(e: Event): void {
+ private _setVolume(value: number) {
this.hass!.callService("media_player", "volume_set", {
entity_id: this.stateObj!.entity_id,
- volume_level: (e.target as any).value / 100,
+ volume_level: value / 100,
});
}
diff --git a/src/panels/media-browser/ha-bar-media-player.ts b/src/panels/media-browser/ha-bar-media-player.ts
index 338f0f2f7d..738749bef5 100644
--- a/src/panels/media-browser/ha-bar-media-player.ts
+++ b/src/panels/media-browser/ha-bar-media-player.ts
@@ -1,5 +1,3 @@
-import "@material/mwc-linear-progress/mwc-linear-progress";
-import type { LinearProgress } from "@material/mwc-linear-progress/mwc-linear-progress";
import {
mdiChevronDown,
mdiMonitor,
@@ -20,11 +18,16 @@ import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { computeStateName } from "../../common/entity/compute_state_name";
import { supportsFeature } from "../../common/entity/supports-feature";
import { debounce } from "../../common/util/debounce";
+import {
+ startMediaProgressInterval,
+ stopMediaProgressInterval,
+} from "../../common/util/media-progress";
+import { VolumeSliderController } from "../../common/util/volume-slider";
import "../../components/ha-button";
-import "../../components/ha-button-menu";
import "../../components/ha-domain-icon";
+import "../../components/ha-dropdown";
+import "../../components/ha-dropdown-item";
import "../../components/ha-icon-button";
-import "../../components/ha-list-item";
import "../../components/ha-slider";
import "../../components/ha-spinner";
import "../../components/ha-state-icon";
@@ -50,6 +53,7 @@ import type { ResolvedMediaSource } from "../../data/media_source";
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../types";
+import type { HaSlider } from "../../components/ha-slider";
import "../lovelace/components/hui-marquee";
import {
BrowserMediaPlayer,
@@ -70,20 +74,40 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
@property({ type: Boolean, reflect: true }) public narrow = false;
- @query("mwc-linear-progress") private _progressBar?: LinearProgress;
+ @query(".progress-slider") private _progressBar?: HaSlider;
@query("#CurrentProgress") private _currentProgress?: HTMLElement;
+ @query(".volume-slider") private _volumeSlider?: HaSlider;
+
@state() private _marqueeActive = false;
@state() private _newMediaExpected = false;
@state() private _browserPlayer?: BrowserMediaPlayer;
+ private _volumeValue = 0;
+
private _progressInterval?: number;
private _browserPlayerVolume = 0.8;
+ private _volumeStep = 2;
+
+ private _debouncedVolumeSet = debounce((value: number) => {
+ this._setVolume(value);
+ }, 100);
+
+ private _volumeController = new VolumeSliderController({
+ getSlider: () => this._volumeSlider,
+ step: this._volumeStep,
+ onSetVolume: (value) => this._setVolume(value),
+ onSetVolumeDebounced: (value) => this._debouncedVolumeSet(value),
+ onValueUpdated: (value) => {
+ this._volumeValue = value;
+ },
+ });
+
public connectedCallback(): void {
super.connectedCallback();
@@ -94,23 +118,20 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
}
if (
- !this._progressInterval &&
this._showProgressBar &&
- stateObj.state === "playing"
+ stateObj.state === "playing" &&
+ !this._progressInterval
) {
- this._progressInterval = window.setInterval(
- () => this._updateProgressBar(),
- 1000
+ this._progressInterval = startMediaProgressInterval(
+ this._progressInterval,
+ () => this._updateProgressBar()
);
}
}
public disconnectedCallback(): void {
super.disconnectedCallback();
- if (this._progressInterval) {
- clearInterval(this._progressInterval);
- this._progressInterval = undefined;
- }
+ this._progressInterval = stopMediaProgressInterval(this._progressInterval);
this._tearDownBrowserPlayer();
}
@@ -174,7 +195,7 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
const stateObj = this._stateObj;
if (!stateObj) {
- return this._renderChoosePlayer(stateObj);
+ return this._renderChoosePlayer(stateObj, this._volumeValue);
}
const controls: ControlButton[] | undefined = !this.narrow
@@ -214,7 +235,6 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
const mediaArt =
stateObj.attributes.entity_picture_local ||
stateObj.attributes.entity_picture;
-
return html`
`
+ ? html`
`
: html`
`}
`}
- ${this._renderChoosePlayer(stateObj)}
+ ${this._renderChoosePlayer(stateObj, this._volumeValue)}
`;
}
- private _renderChoosePlayer(stateObj: MediaPlayerEntity | undefined) {
+ private _renderChoosePlayer(
+ stateObj: MediaPlayerEntity | undefined,
+ volumeValue: number
+ ) {
const isBrowser = this.entityId === BROWSER_PLAYER;
return html`
@@ -294,26 +348,42 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
stateObj &&
supportsFeature(stateObj, MediaPlayerEntityFeature.VOLUME_SET)
? html`
-
+
+
+
+
+
`
: ""
}
-
+
+
@@ -401,6 +469,9 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
) {
this._newMediaExpected = false;
}
+ if (changedProps.has("hass")) {
+ this._updateVolumeValueFromState(this._stateObj);
+ }
}
protected updated(changedProps: PropertyValues) {
@@ -419,23 +490,25 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
const stateObj = this._stateObj;
- this._updateProgressBar();
+ if (this.entityId === BROWSER_PLAYER) {
+ this._updateVolumeValueFromState(stateObj);
+ }
- if (
- !this._progressInterval &&
- this._showProgressBar &&
- stateObj?.state === "playing"
- ) {
- this._progressInterval = window.setInterval(
- () => this._updateProgressBar(),
- 1000
+ this._updateProgressBar();
+ this._syncVolumeSlider();
+
+ if (this._showProgressBar && stateObj?.state === "playing") {
+ this._progressInterval = startMediaProgressInterval(
+ this._progressInterval,
+ () => this._updateProgressBar()
);
} else if (
this._progressInterval &&
(!this._showProgressBar || stateObj?.state !== "playing")
) {
- clearInterval(this._progressInterval);
- this._progressInterval = undefined;
+ this._progressInterval = stopMediaProgressInterval(
+ this._progressInterval
+ );
}
}
@@ -489,25 +562,45 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
private _updateProgressBar(): void {
const stateObj = this._stateObj;
- if (!this._progressBar || !this._currentProgress || !stateObj) {
+ if (!this._progressBar || !stateObj) {
return;
}
if (!stateObj.attributes.media_duration) {
- this._progressBar.progress = 0;
- this._currentProgress.innerHTML = "";
+ this._progressBar.value = 0;
+ if (this._currentProgress) {
+ this._currentProgress.innerHTML = "";
+ }
return;
}
const currentProgress = getCurrentProgress(stateObj);
- this._progressBar.progress =
- currentProgress / stateObj.attributes.media_duration;
+ this._progressBar.max = stateObj.attributes.media_duration;
+ this._progressBar.value = currentProgress;
if (this._currentProgress) {
this._currentProgress.innerHTML = formatMediaTime(currentProgress);
}
}
+ private _updateVolumeValueFromState(stateObj?: MediaPlayerEntity): void {
+ if (!stateObj) {
+ return;
+ }
+ const volumeLevel = stateObj.attributes.volume_level;
+ if (typeof volumeLevel !== "number" || !Number.isFinite(volumeLevel)) {
+ return;
+ }
+ this._volumeValue = Math.round(volumeLevel * 100);
+ }
+
+ private _syncVolumeSlider(): void {
+ if (!this._volumeSlider || this._volumeController.isInteracting) {
+ return;
+ }
+ this._volumeSlider.value = this._volumeValue;
+ }
+
private _handleControlClick(e: MouseEvent): void {
const action = (e.currentTarget! as HTMLElement).getAttribute("action")!;
@@ -526,6 +619,18 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
}
}
+ private _handleMediaSeekChanged(e: Event): void {
+ if (this.entityId === BROWSER_PLAYER || !this._stateObj) {
+ return;
+ }
+
+ const newValue = (e.target as HaSlider).value;
+ this.hass.callService("media_player", "media_seek", {
+ entity_id: this._stateObj.entity_id,
+ seek_position: newValue,
+ });
+ }
+
private _marqueeMouseOver(): void {
if (!this._marqueeActive) {
this._marqueeActive = true;
@@ -538,20 +643,19 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
}
}
- private _selectPlayer(ev: CustomEvent): void {
- const entityId = (ev.currentTarget as any).player;
+ private _handlePlayerSelect(ev: CustomEvent): void {
+ const entityId = (ev.detail.item as any).value;
fireEvent(this, "player-picked", { entityId });
}
- private async _handleVolumeChange(ev) {
- ev.stopPropagation();
- const value = Number(ev.target.value) / 100;
+ private _setVolume(value: number) {
+ const volume = value / 100;
if (this._browserPlayer) {
- this._browserPlayerVolume = value;
- this._browserPlayer.setVolume(value);
- } else {
- await setMediaPlayerVolume(this.hass, this.entityId, value);
+ this._browserPlayerVolume = volume;
+ this._browserPlayer.setVolume(volume);
+ return;
}
+ setMediaPlayerVolume(this.hass, this.entityId, volume);
}
static styles = css`
@@ -570,10 +674,11 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
margin-left: var(--safe-area-inset-left);
}
- mwc-linear-progress {
+ ha-slider {
width: 100%;
- padding: 0 4px;
- --mdc-theme-primary: var(--secondary-text-color);
+ min-width: 100%;
+ --ha-slider-thumb-color: var(--primary-color);
+ --ha-slider-indicator-color: var(--primary-color);
}
ha-button-menu ha-button[slot="trigger"] {
@@ -611,6 +716,7 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
justify-content: flex-end;
align-items: center;
padding: 16px;
+ gap: var(--ha-space-2);
}
.controls {
@@ -633,10 +739,35 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
align-items: center;
}
- mwc-linear-progress[wide] {
+ .progress > div:first-child {
+ margin-right: var(--ha-space-2);
+ }
+
+ .progress > div:last-child {
+ margin-left: var(--ha-space-2);
+ }
+
+ .progress ha-slider {
margin: 0 4px;
}
+ ha-dropdown.volume-menu::part(menu) {
+ width: 220px;
+ max-width: 220px;
+ overflow: visible;
+ padding: 15px 15px;
+ }
+
+ .volume-slider-container {
+ width: 100%;
+ }
+
+ @media (pointer: coarse) {
+ .volume-slider {
+ pointer-events: none;
+ }
+ }
+
.media-info {
text-overflow: ellipsis;
white-space: nowrap;
@@ -700,14 +831,14 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
justify-content: flex-end;
}
- :host([narrow]) mwc-linear-progress {
- padding: 0;
+ :host([narrow]) ha-slider {
position: absolute;
- top: -4px;
+ top: -6px;
left: 0;
+ right: 0;
}
- ha-list-item[selected] {
+ ha-dropdown-item.selected {
font-weight: var(--ha-font-weight-bold);
}
`;