diff --git a/src/components/ha-dialog.ts b/src/components/ha-dialog.ts index f18163ea7a..04dcbb1155 100644 --- a/src/components/ha-dialog.ts +++ b/src/components/ha-dialog.ts @@ -1,11 +1,11 @@ import "@material/mwc-dialog"; import type { Dialog } from "@material/mwc-dialog"; import { style } from "@material/mwc-dialog/mwc-dialog-css"; -import "./ha-icon-button"; -import { css, CSSResult, customElement, html } from "lit-element"; -import type { Constructor, HomeAssistant } from "../types"; import { mdiClose } from "@mdi/js"; +import { css, CSSResult, customElement, html } from "lit-element"; import { computeRTLDirection } from "../common/util/compute_rtl"; +import type { Constructor, HomeAssistant } from "../types"; +import "./ha-icon-button"; const MwcDialog = customElements.get("mwc-dialog") as Constructor; @@ -23,6 +23,10 @@ export const createCloseHeading = (hass: HomeAssistant, title: string) => html` @customElement("ha-dialog") export class HaDialog extends MwcDialog { + public scrollToPos(x: number, y: number) { + this.contentElement.scrollTo(x, y); + } + protected renderHeading() { return html` ${super.renderHeading()} @@ -62,6 +66,10 @@ export class HaDialog extends MwcDialog { position: var(--dialog-surface-position, relative); min-height: var(--mdc-dialog-min-height, auto); } + :host([flexContent]) .mdc-dialog .mdc-dialog__content { + display: flex; + flex-direction: column; + } .header_button { position: absolute; right: 16px; diff --git a/src/components/media-player/dialog-media-player-browse.ts b/src/components/media-player/dialog-media-player-browse.ts index c686f5ef94..5f4d14649a 100644 --- a/src/components/media-player/dialog-media-player-browse.ts +++ b/src/components/media-player/dialog-media-player-browse.ts @@ -15,7 +15,7 @@ import type { } from "../../data/media-player"; import { haStyleDialog } from "../../resources/styles"; import type { HomeAssistant } from "../../types"; -import { createCloseHeading } from "../ha-dialog"; +import "../ha-dialog"; import "./ha-media-player-browse"; import { MediaPlayerBrowseDialogParams } from "./show-media-browser-dialog"; @@ -56,18 +56,17 @@ class DialogMediaPlayerBrowse extends LitElement { scrimClickAction escapeKeyAction hideActions - .heading=${createCloseHeading( - this.hass, - this.hass.localize("ui.components.media-browser.media-player-browser") - )} + flexContent @closed=${this._closeDialog} > @@ -94,13 +93,20 @@ class DialogMediaPlayerBrowse extends LitElement { --dialog-content-padding: 0; } + ha-header-bar { + --mdc-theme-on-primary: var(--primary-text-color); + --mdc-theme-primary: var(--mdc-theme-surface); + flex-shrink: 0; + border-bottom: 1px solid + var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); + } + @media (min-width: 800px) { ha-dialog { --mdc-dialog-max-width: 800px; } ha-media-player-browse { width: 700px; - padding: 20px 24px; } } `, diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index a6056efbca..c485a96453 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -2,7 +2,7 @@ import "@material/mwc-button/mwc-button"; import "@material/mwc-fab/mwc-fab"; import "@material/mwc-list/mwc-list"; import "@material/mwc-list/mwc-list-item"; -import { mdiArrowLeft, mdiFolder, mdiPlay, mdiPlus } from "@mdi/js"; +import { mdiArrowLeft, mdiClose, mdiFolder, mdiPlay, mdiPlus } from "@mdi/js"; import "@polymer/paper-item/paper-item"; import "@polymer/paper-listbox/paper-listbox"; import { @@ -16,8 +16,11 @@ import { PropertyValues, TemplateResult, } from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; +import { ifDefined } from "lit-html/directives/if-defined"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; +import { computeRTLDirection } from "../../common/util/compute_rtl"; import { debounce } from "../../common/util/debounce"; import { browseMediaPlayer, MediaPickedEvent } from "../../data/media-player"; import type { MediaPlayerItem } from "../../data/media-player"; @@ -49,6 +52,12 @@ export class HaMediaPlayerBrowse extends LitElement { @property() public action: "pick" | "play" = "play"; + @property({ type: Boolean }) public hideBack = false; + + @property({ type: Boolean }) public hideTitle = false; + + @property({ type: Boolean }) public dialog = false; + @property({ type: Boolean, attribute: "narrow", reflect: true }) private _narrow = false; @@ -69,6 +78,15 @@ export class HaMediaPlayerBrowse extends LitElement { } } + public navigateBack() { + this._mediaPlayerItems!.pop(); + const item = this._mediaPlayerItems!.pop(); + if (!item) { + return; + } + this._navigate(item); + } + protected render(): TemplateResult { if (!this._mediaPlayerItems.length) { return html``; @@ -90,8 +108,20 @@ export class HaMediaPlayerBrowse extends LitElement { | MediaPlayerItem | undefined = this._hasExpandableChildren(mostRecentItem.children); + const showImages = mostRecentItem.children?.some( + (child) => child.thumbnail && child.thumbnail !== mostRecentItem.thumbnail + ); + + const mediaType = this.hass.localize( + `ui.components.media-browser.content-type.${mostRecentItem.media_content_type}` + ); + return html` -
+
${mostRecentItem.thumbnail ? html` @@ -123,56 +153,65 @@ export class HaMediaPlayerBrowse extends LitElement { ` : html``}
- - ${mostRecentItem?.can_play && - (!this._narrow || (this._narrow && !mostRecentItem.thumbnail)) - ? html` -
- - - ${this.hass.localize( - `ui.components.media-browser.${this.action}` - )} - + ${this.hideTitle && (this._narrow || !mostRecentItem.thumbnail) + ? "" + : html``} + ${mostRecentItem?.can_play && + (!mostRecentItem.thumbnail || !this._narrow) + ? html` + + + ${this.hass.localize( + `ui.components.media-browser.${this.action}` + )} + ` : ""}
+ ${this.dialog + ? html` + + + + ` + : ""}
-
${mostRecentItem.children?.length ? hasExpandableChildren ? html` @@ -184,7 +223,7 @@ export class HaMediaPlayerBrowse extends LitElement {
${mostRecentItem.children.map( - (child) => html` html` + - ${child.title} - -
  • ` + slot="graphic" + > + + + +
    + ${child.title} + +
  • + ` )} ` @@ -260,6 +319,11 @@ export class HaMediaPlayerBrowse extends LitElement { 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 { @@ -295,25 +359,23 @@ export class HaMediaPlayerBrowse extends LitElement { }); } - private async _navigate(ev: MouseEvent): Promise { + private async _navigateForward(ev: MouseEvent): Promise { const target = ev.currentTarget as any; - let item: MediaPlayerItem | undefined; - - if (target.previous) { - this._mediaPlayerItems!.pop(); - item = this._mediaPlayerItems!.pop(); - } - - item = target.item; + const item: MediaPlayerItem = target.item; if (!item) { return; } + this._navigate(item); + } + private async _navigate(item: MediaPlayerItem) { const itemData = await this._fetchData( item.media_content_id, item.media_content_type ); + + this.scrollTo(0, 0); this._mediaPlayerItems = [...this._mediaPlayerItems, itemData]; } @@ -332,7 +394,15 @@ export class HaMediaPlayerBrowse extends LitElement { } private _measureCard(): void { - this._narrow = this.offsetWidth < 500; + 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"); + } } private async _attachObserver(): Promise { @@ -350,22 +420,40 @@ export class HaMediaPlayerBrowse extends LitElement { children.find((item: MediaPlayerItem) => item.can_expand) ); + private _closeDialogAction(): void { + fireEvent(this, "close-dialog"); + } + static get styles(): CSSResultArray { return [ haStyle, css` :host { display: block; + overflow-y: auto; + display: flex; + padding: 0px 0px 20px; + flex-direction: column; } .header { display: flex; justify-content: space-between; + border-bottom: 1px solid var(--divider-color); } - .breadcrumb-overflow { - display: flex; - justify-content: space-between; + .header_button { + position: relative; + top: 14px; + right: -8px; + } + + .header { + background-color: var(--card-background-color); + position: sticky; + top: 0; + z-index: 5; + padding: 20px 24px 10px; } .header-content { @@ -380,6 +468,8 @@ export class HaMediaPlayerBrowse extends LitElement { width: 200px; margin-right: 16px; background-size: cover; + border-radius: 4px; + transition: width 0.4s, height 0.4s; } .header-info { @@ -391,9 +481,14 @@ export class HaMediaPlayerBrowse extends LitElement { flex: 1; } - .header-info .actions { - padding-top: 24px; - --mdc-theme-primary: var(--primary-color); + .header-info mwc-button { + display: block; + } + + .breadcrumb-overflow { + display: flex; + flex-grow: 1; + justify-content: space-between; } .breadcrumb { @@ -404,7 +499,7 @@ export class HaMediaPlayerBrowse extends LitElement { } .breadcrumb .title { - font-size: 48px; + font-size: 32px; line-height: 1.2; font-weight: bold; margin: 0; @@ -412,6 +507,7 @@ export class HaMediaPlayerBrowse extends LitElement { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; + padding-right: 8px; } .breadcrumb .previous-title { @@ -428,17 +524,8 @@ export class HaMediaPlayerBrowse extends LitElement { font-size: 16px; overflow: hidden; text-overflow: ellipsis; - } - - .divider { - padding: 10px 0; - } - - .divider::before { - height: 1px; - display: block; - background-color: var(--divider-color); - content: " "; + margin-bottom: 0; + transition: height 0.5s, margin 0.5s; } /* ============= CHILDREN ============= */ @@ -446,8 +533,7 @@ export class HaMediaPlayerBrowse extends LitElement { mwc-list { --mdc-list-vertical-padding: 0; --mdc-theme-text-icon-on-background: var(--secondary-text-color); - border: 1px solid var(--divider-color); - border-radius: 4px; + margin-top: 10px; } mwc-list li:last-child { @@ -468,6 +554,10 @@ export class HaMediaPlayerBrowse extends LitElement { margin: 8px 0px; } + :host(:not([narrow])) .children { + padding: 0px 24px; + } + .child { display: flex; flex-direction: column; @@ -505,7 +595,7 @@ export class HaMediaPlayerBrowse extends LitElement { bottom: 4px; right: 4px; transition: all 0.5s; - background-color: rgba(255, 255, 255, 0.5); + background-color: rgba(var(--rgb-card-background-color), 0.5); border-radius: 50%; } @@ -531,22 +621,46 @@ export class HaMediaPlayerBrowse extends LitElement { color: var(--secondary-text-color); } + mwc-list-item .graphic { + background-size: cover; + } + + mwc-list-item .graphic .play { + opacity: 0; + transition: all 0.5s; + background-color: rgba(var(--rgb-card-background-color), 0.5); + border-radius: 50%; + --mdc-icon-button-size: 40px; + } + + mwc-list-item:hover .graphic .play { + opacity: 1; + color: var(--primary-color); + } + + mwc-list-item .graphic .play.show { + opacity: 1; + background-color: transparent; + } + /* ============= Narrow ============= */ :host([narrow]) { padding: 0; } - :host([narrow]) mwc-list { - border: 0; - } - :host([narrow]) .breadcrumb .title { - font-size: 38px; + font-size: 24px; } - :host([narrow]) .breadcrumb-overflow { - align-items: flex-end; + :host([narrow]) .header { + padding: 0; + } + + :host([narrow]) .header_button { + position: absolute; + top: 14px; + right: 8px; } :host([narrow]) .header-content { @@ -558,26 +672,100 @@ export class HaMediaPlayerBrowse extends LitElement { height: auto; width: 100%; margin-right: 0; - padding-bottom: 100%; + padding-bottom: 50%; margin-bottom: 8px; position: relative; + background-position: center; + border-radius: 0; + transition: width 0.4s, height 0.4s, padding-bottom 0.4s; } - :host([narrow]) .header-content .img mwc-fab { + mwc-fab { position: absolute; --mdc-theme-secondary: var(--primary-color); bottom: -20px; right: 20px; } - :host([narrow]) .header-info, + :host([narrow]) .header-info mwc-button { + margin-top: 16px; + margin-bottom: 8px; + } + + :host([narrow]) .header-info { + padding: 20px 24px 10px; + } + :host([narrow]) .media-source, :host([narrow]) .children { padding: 0 24px; } :host([narrow]) .children { - grid-template-columns: 1fr 1fr !important; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) !important; + } + + /* ============= Scroll ============= */ + + :host([scroll]) .breadcrumb .subtitle { + height: 0; + margin: 0; + } + + :host([scroll]) .breadcrumb .title { + -webkit-line-clamp: 1; + } + + :host([scroll]) .header-info mwc-button, + .no-img .header-info mwc-button { + padding-right: 4px; + } + + :host([scroll][narrow]) .no-img .header-info mwc-button { + padding-right: 16px; + } + + :host([scroll]) .header-info { + flex-direction: row; + } + + :host([scroll]) .header-info mwc-button { + align-self: center; + margin-top: 0; + margin-bottom: 0; + } + + :host([scroll][narrow]) .no-img .header-info { + flex-direction: row-reverse; + } + + :host([scroll][narrow]) .header-info { + padding: 20px 24px 10px 24px; + align-items: center; + } + + :host([scroll]) .header-content { + align-items: flex-end; + flex-direction: row; + } + + :host([scroll]) .header-content .img { + height: 75px; + width: 75px; + } + + :host([scroll][narrow]) .header-content .img { + height: 100px; + width: 100px; + padding-bottom: initial; + margin-bottom: 0; + } + + :host([scroll]) mwc-fab { + bottom: 4px; + right: 4px; + --mdc-fab-box-shadow: none; + --mdc-theme-secondary: rgba(var(--rgb-primary-color), 0.5); } `, ];