Add media title popups.
This commit is contained in:
+15
-2
@@ -21,6 +21,7 @@ import {
|
||||
} from '../types.js';
|
||||
import { EmblaOptionsType, EmblaPluginType } from 'embla-carousel';
|
||||
import { HomeAssistant } from 'custom-card-helpers';
|
||||
import JSMpeg from '@cycjimmy/jsmpeg-player';
|
||||
import { Ref, createRef, ref } from 'lit/directives/ref.js';
|
||||
import { Task } from '@lit-labs/task';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
@@ -47,7 +48,8 @@ import {
|
||||
} from '../common.js';
|
||||
import { renderProgressIndicator } from '../components/message.js';
|
||||
|
||||
import JSMpeg from '@cycjimmy/jsmpeg-player';
|
||||
import './next-prev-control.js';
|
||||
import './title-control.js';
|
||||
|
||||
import liveStyle from '../scss/live.scss';
|
||||
import liveFrigateStyle from '../scss/live-frigate.scss';
|
||||
@@ -471,7 +473,7 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
|
||||
protected render(): TemplateResult | void {
|
||||
const [slides, cameraToSlide] = this._getSlides();
|
||||
this._cameraToSlide = cameraToSlide;
|
||||
if (!slides || !this.liveConfig) {
|
||||
if (!slides || !this.liveConfig || !this.cameras || !this.view) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -482,6 +484,8 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
|
||||
) as LiveConfig;
|
||||
|
||||
const [prev, next] = this._getCameraNeighbors();
|
||||
const title = getCameraTitle(this.hass, this.cameras.get(this.view.camera));
|
||||
|
||||
return html`
|
||||
<div class="embla">
|
||||
<frigate-card-next-previous-control
|
||||
@@ -512,6 +516,15 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
|
||||
>
|
||||
</frigate-card-next-previous-control>
|
||||
</div>
|
||||
<frigate-card-title-control
|
||||
${ref(this._titleControlRef)}
|
||||
.config=${config.controls.title}
|
||||
.text="${title
|
||||
? `${localize('common.live')}: ${title}`
|
||||
: ''}"
|
||||
.fitInto=${this as HTMLElement}
|
||||
>
|
||||
</frigate-card-title-control>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import './next-prev-control.js';
|
||||
import mediaCarouselStyle from '../scss/media-carousel.scss';
|
||||
|
||||
import { FrigateCardNextPreviousControl } from './next-prev-control.js';
|
||||
import { FrigateCardTitleControl } from './title-control.js';
|
||||
import { MediaAutoPlayPauseType } from './embla-plugins/media-autoplay.js';
|
||||
|
||||
const getEmptyImageSrc = (width: number, height: number) =>
|
||||
@@ -27,7 +28,8 @@ export class FrigateCardMediaCarousel extends FrigateCardCarousel {
|
||||
protected _mediaShowInfo: Record<number, MediaShowInfo> = {};
|
||||
protected _nextControlRef: Ref<FrigateCardNextPreviousControl> = createRef();
|
||||
protected _previousControlRef: Ref<FrigateCardNextPreviousControl> = createRef();
|
||||
|
||||
protected _titleControlRef: Ref<FrigateCardTitleControl> = createRef();
|
||||
protected _titleTimerID: number | null = null;
|
||||
/**
|
||||
* Play the media on the selected slide. May be overridden to control when
|
||||
* autoplay should happen.
|
||||
@@ -36,6 +38,32 @@ export class FrigateCardMediaCarousel extends FrigateCardCarousel {
|
||||
(this._plugins['MediaAutoPlayPause'] as MediaAutoPlayPauseType | undefined)?.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the media title after the media loads.
|
||||
*/
|
||||
protected _titleHandler(): void {
|
||||
const show = () => {
|
||||
this._titleTimerID = null;
|
||||
this._titleControlRef.value?.show();
|
||||
};
|
||||
|
||||
if (this._titleTimerID) {
|
||||
window.clearTimeout(this._titleTimerID);
|
||||
}
|
||||
if (this._titleControlRef.value?.isVisible()) {
|
||||
// If it's already visible, update it immediately (but also update it
|
||||
// after the timer expires to ensure it re-positions if necessary, see
|
||||
// comment below).
|
||||
show();
|
||||
}
|
||||
|
||||
// Allow a brief pause after the media loads, but before the title is
|
||||
// displayed. This allows for a pleasant appearance/disappear of the title,
|
||||
// and allows for the browser to finish rendering the carousel (inc.
|
||||
// adaptive height which has `0.5s ease`, see `media-carousel.scss`).
|
||||
this._titleTimerID = window.setTimeout(show, 0.5 * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component connected callback.
|
||||
*/
|
||||
@@ -43,6 +71,7 @@ export class FrigateCardMediaCarousel extends FrigateCardCarousel {
|
||||
super.connectedCallback();
|
||||
this.addEventListener('frigate-card:media-show', this._autoplayHandler);
|
||||
this.addEventListener('frigate-card:media-show', this._adaptiveHeightHandler);
|
||||
this.addEventListener('frigate-card:media-show', this._titleHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,6 +81,7 @@ export class FrigateCardMediaCarousel extends FrigateCardCarousel {
|
||||
super.disconnectedCallback();
|
||||
this.removeEventListener('frigate-card:media-show', this._autoplayHandler);
|
||||
this.removeEventListener('frigate-card:media-show', this._adaptiveHeightHandler);
|
||||
this.removeEventListener('frigate-card:media-show', this._titleHandler);
|
||||
}
|
||||
|
||||
protected _destroyCarousel(): void {
|
||||
@@ -93,7 +123,7 @@ export class FrigateCardMediaCarousel extends FrigateCardCarousel {
|
||||
* actually the media load/show that will change the dimensions, and that is
|
||||
* async from carousel actions (e.g. lazy-loaded media).
|
||||
*/
|
||||
protected _adaptiveHeightHandler(): void {
|
||||
protected _adaptiveHeightHandler(): void {
|
||||
const adaptCarouselHeight = (): void => {
|
||||
if (!this._carousel) {
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
||||
import { createRef, ref, Ref } from 'lit/directives/ref';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
|
||||
import { TitleControlConfig } from '../types.js';
|
||||
|
||||
import titleStyle from '../scss/title-control.scss';
|
||||
|
||||
type PaperToast = HTMLElement & {
|
||||
opened: boolean;
|
||||
};
|
||||
|
||||
@customElement('frigate-card-title-control')
|
||||
export class FrigateCardTitleControl extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public config?: TitleControlConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
public text?: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
public fitInto?: HTMLElement;
|
||||
|
||||
protected _toastRef: Ref<PaperToast> = createRef();
|
||||
|
||||
/**
|
||||
* Master render method.
|
||||
* @returns A rendered template.
|
||||
*/
|
||||
protected render(): TemplateResult {
|
||||
if (!this.text || !this.config || this.config.mode == 'none' || !this.fitInto) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const verticalAlign = this.config.mode.match(/-top-/) ? 'top' : 'bottom';
|
||||
const horizontalAlign = this.config.mode.match(/-left$/) ? 'left' : 'right';
|
||||
|
||||
return html` <paper-toast
|
||||
${ref(this._toastRef)}
|
||||
class="capsule"
|
||||
.duration=${this.config.duration_seconds * 1000}
|
||||
.verticalAlign=${verticalAlign}
|
||||
.horizontalAlign=${horizontalAlign}
|
||||
.text="${this.text}"
|
||||
.fitInto=${this.fitInto}
|
||||
>
|
||||
</paper-toast>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the toast is visible.
|
||||
* @returns `true` if the toast is visible, `false` otherwise.
|
||||
*/
|
||||
public isVisible(): boolean {
|
||||
return this._toastRef.value?.opened ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the toast.
|
||||
*/
|
||||
public hide(): void {
|
||||
if (this._toastRef.value) {
|
||||
// Set it to false first, to ensure the timer resets.
|
||||
this._toastRef.value.opened = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the toast.
|
||||
*/
|
||||
public show(): void {
|
||||
if (this._toastRef.value) {
|
||||
// Set it to false first, to ensure the timer resets.
|
||||
this._toastRef.value.opened = false;
|
||||
this._toastRef.value.opened = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get element styles.
|
||||
*/
|
||||
static get styles(): CSSResultGroup {
|
||||
return unsafeCSS(titleStyle);
|
||||
}
|
||||
}
|
||||
+35
-25
@@ -41,6 +41,7 @@ import {
|
||||
import { renderProgressIndicator } from '../components/message.js';
|
||||
|
||||
import './next-prev-control.js';
|
||||
import './title-control.js';
|
||||
|
||||
import viewerStyle from '../scss/viewer.scss';
|
||||
import viewerCoreStyle from '../scss/viewer-core.scss';
|
||||
@@ -628,32 +629,41 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
||||
const [prev, next] = [neighbors?.previous, neighbors?.next];
|
||||
|
||||
return html`<div class="embla">
|
||||
<frigate-card-next-previous-control
|
||||
${ref(this._previousControlRef)}
|
||||
.direction=${'previous'}
|
||||
.controlConfig=${this.viewerConfig?.controls.next_previous}
|
||||
.thumbnail=${prev && prev.thumbnail ? prev.thumbnail : undefined}
|
||||
.label=${prev ? prev.title : ''}
|
||||
?disabled=${!prev}
|
||||
@click=${() => {
|
||||
this._nextPreviousHandler('previous');
|
||||
}}
|
||||
></frigate-card-next-previous-control>
|
||||
<div class="embla__viewport">
|
||||
<div class="embla__container">${slides}</div>
|
||||
<frigate-card-next-previous-control
|
||||
${ref(this._previousControlRef)}
|
||||
.direction=${'previous'}
|
||||
.controlConfig=${this.viewerConfig?.controls.next_previous}
|
||||
.thumbnail=${prev && prev.thumbnail ? prev.thumbnail : undefined}
|
||||
.label=${prev ? prev.title : ''}
|
||||
?disabled=${!prev}
|
||||
@click=${() => {
|
||||
this._nextPreviousHandler('previous');
|
||||
}}
|
||||
></frigate-card-next-previous-control>
|
||||
<div class="embla__viewport">
|
||||
<div class="embla__container">${slides}</div>
|
||||
</div>
|
||||
<frigate-card-next-previous-control
|
||||
${ref(this._nextControlRef)}
|
||||
.direction=${'next'}
|
||||
.controlConfig=${this.viewerConfig?.controls.next_previous}
|
||||
.thumbnail=${next && next.thumbnail ? next.thumbnail : undefined}
|
||||
.label=${next ? next.title : ''}
|
||||
?disabled=${!next}
|
||||
@click=${() => {
|
||||
this._nextPreviousHandler('next');
|
||||
}}
|
||||
></frigate-card-next-previous-control>
|
||||
</div>
|
||||
<frigate-card-next-previous-control
|
||||
${ref(this._nextControlRef)}
|
||||
.direction=${'next'}
|
||||
.controlConfig=${this.viewerConfig?.controls.next_previous}
|
||||
.thumbnail=${next && next.thumbnail ? next.thumbnail : undefined}
|
||||
.label=${next ? next.title : ''}
|
||||
?disabled=${!next}
|
||||
@click=${() => {
|
||||
this._nextPreviousHandler('next');
|
||||
}}
|
||||
></frigate-card-next-previous-control>
|
||||
</div>`;
|
||||
${this.view?.media
|
||||
? html` <frigate-card-title-control
|
||||
${ref(this._titleControlRef)}
|
||||
.config=${this.viewerConfig?.controls.title}
|
||||
.text="${this.view.media.title}"
|
||||
.fitInto=${this as HTMLElement}
|
||||
>
|
||||
</frigate-card-title-control>`
|
||||
: ``} `;
|
||||
}
|
||||
|
||||
protected _renderMediaItem(
|
||||
|
||||
Reference in New Issue
Block a user