diff --git a/src/components/media-player/dialog-media-player-browse.ts b/src/components/media-player/dialog-media-player-browse.ts
index 311536844f..3ef4f00c04 100644
--- a/src/components/media-player/dialog-media-player-browse.ts
+++ b/src/components/media-player/dialog-media-player-browse.ts
@@ -90,14 +90,20 @@ class DialogMediaPlayerBrowse extends LitElement {
--dialog-content-padding: 0;
}
+ ha-media-player-browse {
+ --media-browser-max-height: 100vh;
+ }
+
@media (min-width: 800px) {
ha-dialog {
--mdc-dialog-max-width: 800px;
--dialog-surface-position: fixed;
--dialog-surface-top: 40px;
- --mdc-dialog-max-height: calc(100% - 72px);
+ --mdc-dialog-max-height: calc(100vh - 72px);
}
ha-media-player-browse {
+ position: initial;
+ --media-browser-max-height: 100vh - 72px;
width: 700px;
}
}
diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts
index 53dfc5c230..32146ec822 100644
--- a/src/components/media-player/ha-media-player-browse.ts
+++ b/src/components/media-player/ha-media-player-browse.ts
@@ -10,11 +10,13 @@ import {
css,
CSSResultArray,
customElement,
+ eventOptions,
html,
internalProperty,
LitElement,
property,
PropertyValues,
+ query,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
@@ -67,12 +69,21 @@ export class HaMediaPlayerBrowse extends LitElement {
@property({ type: Boolean, attribute: "narrow", reflect: true })
private _narrow = false;
+ @property({ type: Boolean, attribute: "scroll", reflect: true })
+ private _scrolled = false;
+
@internalProperty() private _loading = false;
@internalProperty() private _error?: { message: string; code: string };
@internalProperty() private _mediaPlayerItems: MediaPlayerItem[] = [];
+ @query(".header") private _header?: HTMLDivElement;
+
+ @query(".content") private _content?: HTMLDivElement;
+
+ private _headerOffsetHeight = 0;
+
private _resizeObserver?: ResizeObserver;
public connectedCallback(): void {
@@ -140,274 +151,283 @@ export class HaMediaPlayerBrowse extends LitElement {
return html`
- ${this._error
- ? html`
-
- ${this._renderError(this._error)}
-
- `
- : currentItem.children?.length
- ? childrenMediaClass.layout === "grid"
- ? html`
-
- `
: html`
-
- ${currentItem.children.map(
- (child) => html`
-
-
-
-
-
-
- ${child.title}
-
-
- `
- )}
-
- `
- : html`
-
- ${this.hass.localize("ui.components.media-browser.no_items")}
- ${currentItem.media_content_id ===
- "media-source://media_source/local/."
- ? html`
${this.hass.localize(
- "ui.components.media-browser.learn_adding_local_media",
- "documentation",
- html`
${this.hass.localize(
- "ui.components.media-browser.documentation"
- )}`
- )}
-
- ${this.hass.localize(
- "ui.components.media-browser.local_media_files"
- )}.`
- : ""}
-
- `}
+
+ ${this.hass.localize("ui.components.media-browser.no_items")}
+ ${currentItem.media_content_id ===
+ "media-source://media_source/local/."
+ ? html`
${this.hass.localize(
+ "ui.components.media-browser.learn_adding_local_media",
+ "documentation",
+ html`
${this.hass.localize(
+ "ui.components.media-browser.documentation"
+ )}`
+ )}
+
+ ${this.hass.localize(
+ "ui.components.media-browser.local_media_files"
+ )}.`
+ : ""}
+
+ `}
+
`;
}
protected firstUpdated(): void {
this._measureCard();
this._attachObserver();
-
- this.addEventListener("scroll", this._scroll, { passive: true });
- this.addEventListener("touchmove", this._scroll, {
- passive: true,
- });
}
protected updated(changedProps: PropertyValues): void {
super.updated(changedProps);
+ if (
+ changedProps.has("_mediaPlayerItems") &&
+ this._mediaPlayerItems.length
+ ) {
+ this._setHeaderHeight();
+ }
+
+ if (
+ changedProps.get("_scrolled") !== undefined &&
+ this._mediaPlayerItems.length
+ ) {
+ this._animateHeaderHeight();
+ }
+
if (
!changedProps.has("entityId") &&
!changedProps.has("mediaContentId") &&
@@ -435,6 +455,33 @@ export class HaMediaPlayerBrowse extends LitElement {
});
}
+ private async _setHeaderHeight() {
+ await this.updateComplete;
+ const header = this._header;
+ const content = this._content;
+ if (!header || !content) {
+ return;
+ }
+ this._headerOffsetHeight = header.offsetHeight;
+ content.style.marginTop = `${this._headerOffsetHeight}px`;
+ content.style.maxHeight = `calc(var(--media-browser-max-height, 100%) - ${this._headerOffsetHeight}px)`;
+ }
+
+ private _animateHeaderHeight() {
+ let start;
+ const animate = (time) => {
+ if (start === undefined) {
+ start = time;
+ }
+ const elapsed = time - start;
+ this._setHeaderHeight();
+ if (elapsed < 400) {
+ requestAnimationFrame(animate);
+ }
+ };
+ requestAnimationFrame(animate);
+ }
+
private _actionClicked(ev: MouseEvent): void {
ev.stopPropagation();
const item = (ev.currentTarget as any).item;
@@ -482,7 +529,8 @@ export class HaMediaPlayerBrowse extends LitElement {
return;
}
- this.scrollTo(0, 0);
+ this._content?.scrollTo(0, 0);
+ this._scrolled = false;
this._mediaPlayerItems = [...this._mediaPlayerItems, itemData];
}
@@ -509,11 +557,13 @@ export class HaMediaPlayerBrowse extends LitElement {
this._narrow = (this.dialog ? window.innerWidth : this.offsetWidth) < 450;
}
- private _scroll(): void {
- if (this.scrollTop > (this._narrow ? 224 : 125)) {
- this.setAttribute("scroll", "");
- } else if (this.scrollTop === 0) {
- this.removeAttribute("scroll");
+ @eventOptions({ passive: true })
+ private _scroll(ev: Event): void {
+ const content = ev.currentTarget as HTMLDivElement;
+ if (!this._scrolled && content.scrollTop > this._headerOffsetHeight) {
+ this._scrolled = true;
+ } else if (this._scrolled && content.scrollTop < this._headerOffsetHeight) {
+ this._scrolled = false;
}
}
@@ -571,38 +621,44 @@ export class HaMediaPlayerBrowse extends LitElement {
haStyle,
css`
:host {
- display: block;
- overflow-y: auto;
display: flex;
- padding: 0px 0px 20px;
flex-direction: column;
+ position: relative;
}
ha-circular-progress {
--mdc-theme-primary: var(--primary-color);
display: flex;
justify-content: center;
- margin-top: 40px;
+ margin: 40px;
}
.container {
padding: 16px;
}
+ .content {
+ overflow-y: auto;
+ padding-bottom: 20px;
+ box-sizing: border-box;
+ }
+
.header {
- display: block;
+ display: flex;
justify-content: space-between;
border-bottom: 1px solid var(--divider-color);
background-color: var(--card-background-color);
- position: sticky;
- position: -webkit-sticky;
+ position: absolute;
top: 0;
+ right: 0;
+ left: 0;
z-index: 5;
padding: 20px 24px 10px;
}
- .header-wrapper {
- display: flex;
+ .header_button {
+ position: relative;
+ right: -8px;
}
.header-content {
@@ -696,8 +752,8 @@ export class HaMediaPlayerBrowse extends LitElement {
minmax(var(--media-browse-item-size, 175px), 0.1fr)
);
grid-gap: 16px;
- margin: 8px 0px;
padding: 0px 24px;
+ margin: 8px 0px;
}
:host([dialog]) .children {
diff --git a/src/panels/media-browser/ha-panel-media-browser.ts b/src/panels/media-browser/ha-panel-media-browser.ts
index 32e8661d52..7409a1e7d0 100644
--- a/src/panels/media-browser/ha-panel-media-browser.ts
+++ b/src/panels/media-browser/ha-panel-media-browser.ts
@@ -137,7 +137,7 @@ class PanelMediaBrowser extends LitElement {
--mdc-theme-primary: var(--app-header-text-color);
}
ha-media-player-browse {
- height: calc(100vh - 84px);
+ height: calc(100vh - 64px);
}
:host([narrow]) app-toolbar mwc-button {
width: 65px;