Skeleton live carousel.
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
import type { BrowseMediaQueryParameters, BrowseMediaSource, ExtendedHomeAssistant } from './types.js';
|
||||
import type {
|
||||
BrowseMediaQueryParameters,
|
||||
BrowseMediaSource,
|
||||
CameraConfig,
|
||||
ExtendedHomeAssistant,
|
||||
} from './types.js';
|
||||
import { HomeAssistant } from 'custom-card-helpers';
|
||||
import { homeAssistantWSRequest } from './common.js';
|
||||
import { browseMediaSourceSchema } from './types.js';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import dayjs_custom_parse_format from 'dayjs/plugin/customParseFormat.js';
|
||||
import { View } from './view.js';
|
||||
|
||||
dayjs.extend(dayjs_custom_parse_format);
|
||||
|
||||
@@ -16,7 +22,8 @@ export class BrowseMediaUtil {
|
||||
*/
|
||||
static extractEventID(media: BrowseMediaSource): string | null {
|
||||
const result = media.media_content_id.match(
|
||||
/^media-source:\/\/frigate\/.*\/(?<id>[.0-9]+-[a-zA-Z0-9]+)$/);
|
||||
/^media-source:\/\/frigate\/.*\/(?<id>[.0-9]+-[a-zA-Z0-9]+)$/,
|
||||
);
|
||||
return result && result.groups ? result.groups['id'] : null;
|
||||
}
|
||||
|
||||
@@ -25,9 +32,7 @@ export class BrowseMediaUtil {
|
||||
* @param browseMedia The media object to extract the start time from.
|
||||
* @returns The start time in unix/epoch time, or null if it cannot be determined.
|
||||
*/
|
||||
static extractEventStartTime(
|
||||
browseMedia: BrowseMediaSource,
|
||||
): number | null {
|
||||
static extractEventStartTime(browseMedia: BrowseMediaSource): number | null {
|
||||
// Example: 2021-08-27 20:57:22 [10s, Person 76%]
|
||||
const result = browseMedia.title.match(/^(?<iso_datetime>.+) \[/);
|
||||
if (result && result.groups) {
|
||||
@@ -57,9 +62,7 @@ export class BrowseMediaUtil {
|
||||
* @param media The media object with children.
|
||||
* @returns The first true media item found.
|
||||
*/
|
||||
static getFirstTrueMediaChildIndex(
|
||||
media: BrowseMediaSource | null,
|
||||
): number | null {
|
||||
static getFirstTrueMediaChildIndex(media: BrowseMediaSource | null): number | null {
|
||||
if (!media || !media.children) {
|
||||
return null;
|
||||
}
|
||||
@@ -117,4 +120,41 @@ export class BrowseMediaUtil {
|
||||
].join('/'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameters to search for media.
|
||||
* @returns A BrowseMediaQueryParameters object.
|
||||
*/
|
||||
static getBrowseMediaQueryParameters(
|
||||
mediaType: 'clips' | 'snapshots',
|
||||
cameraConfig?: CameraConfig,
|
||||
): BrowseMediaQueryParameters | undefined {
|
||||
if (!cameraConfig || !cameraConfig.camera_name) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
mediaType: mediaType,
|
||||
clientId: cameraConfig.client_id,
|
||||
cameraName: cameraConfig.camera_name,
|
||||
label: cameraConfig.label,
|
||||
zone: cameraConfig.zone,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameters to search for media related to the current view.
|
||||
* @returns A BrowseMediaQueryParameters object.
|
||||
*/
|
||||
static getBrowseMediaQueryParametersFromView(
|
||||
view: View,
|
||||
cameraConfig: CameraConfig,
|
||||
): BrowseMediaQueryParameters | undefined {
|
||||
if (!view.isClipRelatedView() && !view.isSnapshotRelatedView()) {
|
||||
return undefined;
|
||||
}
|
||||
return BrowseMediaUtil.getBrowseMediaQueryParameters(
|
||||
view.isClipRelatedView() ? 'clips' : 'snapshots',
|
||||
cameraConfig,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-36
@@ -884,36 +884,6 @@ export class FrigateCard extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameters to search for media related to the current view.
|
||||
* @returns A BrowseMediaQueryParameters object.
|
||||
*/
|
||||
protected _getBrowseMediaQueryParameters(
|
||||
mediaType?: 'clips' | 'snapshots',
|
||||
): BrowseMediaQueryParameters | undefined {
|
||||
const cameraConfig = this._getSelectedCameraConfig();
|
||||
|
||||
if (
|
||||
!cameraConfig ||
|
||||
!cameraConfig.camera_name ||
|
||||
!this._view ||
|
||||
!(
|
||||
this._view.isClipRelatedView() ||
|
||||
this._view.isSnapshotRelatedView() ||
|
||||
mediaType
|
||||
)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
mediaType: mediaType || (this._view.isClipRelatedView() ? 'clips' : 'snapshots'),
|
||||
clientId: cameraConfig.client_id,
|
||||
cameraName: cameraConfig.camera_name,
|
||||
label: cameraConfig.label,
|
||||
zone: cameraConfig.zone,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for media play event.
|
||||
*/
|
||||
@@ -1158,7 +1128,10 @@ export class FrigateCard extends LitElement {
|
||||
? html` <frigate-card-gallery
|
||||
.hass=${this._hass}
|
||||
.view=${this._view}
|
||||
.browseMediaQueryParameters=${this._getBrowseMediaQueryParameters()}
|
||||
.browseMediaQueryParameters=${BrowseMediaUtil.getBrowseMediaQueryParametersFromView(
|
||||
this._view,
|
||||
cameraConfig,
|
||||
)}
|
||||
class="${classMap(galleryClasses)}"
|
||||
@frigate-card:change-view=${this._changeViewHandler}
|
||||
@frigate-card:message=${this._messageHandler}
|
||||
@@ -1169,7 +1142,10 @@ export class FrigateCard extends LitElement {
|
||||
? html` <frigate-card-viewer
|
||||
.hass=${this._hass}
|
||||
.view=${this._view}
|
||||
.browseMediaQueryParameters=${this._getBrowseMediaQueryParameters()}
|
||||
.browseMediaQueryParameters=${BrowseMediaUtil.getBrowseMediaQueryParametersFromView(
|
||||
this._view,
|
||||
cameraConfig,
|
||||
)}
|
||||
.viewerConfig=${this.config.event_viewer}
|
||||
.resolvedMediaCache=${this._resolvedMediaCache}
|
||||
class="${classMap(viewerClasses)}"
|
||||
@@ -1189,11 +1165,8 @@ export class FrigateCard extends LitElement {
|
||||
<frigate-card-live
|
||||
.hass=${this._hass}
|
||||
.view=${this._view}
|
||||
.browseMediaQueryParameters=${this._getBrowseMediaQueryParameters(
|
||||
this.config.live.controls.thumbnails.media,
|
||||
)}
|
||||
.liveConfig=${this.config.live}
|
||||
.cameraConfig=${cameraConfig}
|
||||
.cameras=${this._cameras}
|
||||
.preload=${this.config.live.preload && !this._view.is('live')}
|
||||
class="${classMap(liveClasses)}"
|
||||
@frigate-card:change-view=${this._changeViewHandler}
|
||||
|
||||
+17
-14
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
CSSResultGroup,
|
||||
LitElement,
|
||||
unsafeCSS,
|
||||
PropertyValues,
|
||||
} from 'lit';
|
||||
import { CSSResultGroup, LitElement, unsafeCSS, PropertyValues } from 'lit';
|
||||
import EmblaCarousel, { EmblaCarouselType, EmblaOptionsType } from 'embla-carousel';
|
||||
|
||||
import { dispatchFrigateCardEvent } from '../common';
|
||||
@@ -15,7 +10,6 @@ export interface CarouselSelect {
|
||||
}
|
||||
|
||||
export class FrigateCardCarousel extends LitElement {
|
||||
protected _options?: EmblaOptionsType;
|
||||
protected _carousel?: EmblaCarouselType;
|
||||
|
||||
/**
|
||||
@@ -41,11 +35,17 @@ export class FrigateCardCarousel extends LitElement {
|
||||
updated(changedProperties: PropertyValues): void {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (!this._carousel) {
|
||||
this.updateComplete.then(() => {
|
||||
this._loadCarousel();
|
||||
});
|
||||
}
|
||||
this.updateComplete.then(() => {
|
||||
this._loadCarousel();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Embla options to use.
|
||||
* @returns An EmblaOptionsType object or undefined for no options.
|
||||
*/
|
||||
protected _getOptions(): EmblaOptionsType | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,8 +56,11 @@ export class FrigateCardCarousel extends LitElement {
|
||||
'.embla__viewport',
|
||||
) as HTMLElement;
|
||||
|
||||
if (!this._carousel && carouselNode) {
|
||||
this._carousel = EmblaCarousel(carouselNode, this._options);
|
||||
if (carouselNode) {
|
||||
if (this._carousel) {
|
||||
this._carousel.destroy();
|
||||
}
|
||||
this._carousel = EmblaCarousel(carouselNode, this._getOptions());
|
||||
this._carousel.on('init', () => dispatchFrigateCardEvent(this, 'carousel:init'));
|
||||
this._carousel.on('select', () => {
|
||||
const selected = this.carouselSelected();
|
||||
|
||||
+212
-35
@@ -1,6 +1,10 @@
|
||||
// TODO controls
|
||||
// TODO media events
|
||||
// TODO lazy loading
|
||||
// TODO height adapting
|
||||
|
||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
||||
import type {
|
||||
BrowseMediaQueryParameters,
|
||||
BrowseMediaSource,
|
||||
ExtendedHomeAssistant,
|
||||
CameraConfig,
|
||||
@@ -9,11 +13,13 @@ import type {
|
||||
MediaShowInfo,
|
||||
WebRTCConfig,
|
||||
} from '../types.js';
|
||||
import { EmblaCarouselType, EmblaOptionsType } from 'embla-carousel';
|
||||
import { HomeAssistant } from 'custom-card-helpers';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { until } from 'lit/directives/until.js';
|
||||
|
||||
import { BrowseMediaUtil } from '../browse-media-util.js';
|
||||
import { FrigateCardCarousel } from './carousel.js';
|
||||
import { ThumbnailCarouselTap } from './thumbnail-carousel.js';
|
||||
import { View } from '../view.js';
|
||||
import { localize } from '../localize/localize.js';
|
||||
@@ -34,6 +40,7 @@ import liveStyle from '../scss/live.scss';
|
||||
import liveFrigateStyle from '../scss/live-frigate.scss';
|
||||
import liveJSMPEGStyle from '../scss/live-jsmpeg.scss';
|
||||
import liveWebRTCStyle from '../scss/live-webrtc.scss';
|
||||
import mediaCarouselStyle from '../scss/media-carousel.scss';
|
||||
|
||||
// Number of seconds a signed URL is valid for.
|
||||
const URL_SIGN_EXPIRY_SECONDS = 24 * 60 * 60;
|
||||
@@ -50,14 +57,11 @@ export class FrigateCardLive extends LitElement {
|
||||
protected view?: View;
|
||||
|
||||
@property({ attribute: false })
|
||||
protected cameraConfig?: CameraConfig;
|
||||
protected cameras?: Map<string, CameraConfig>;
|
||||
|
||||
@property({ attribute: false })
|
||||
protected liveConfig?: LiveConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
protected browseMediaQueryParameters?: BrowseMediaQueryParameters;
|
||||
|
||||
@property({ attribute: false })
|
||||
set preload(preload: boolean) {
|
||||
this._preload = preload;
|
||||
@@ -97,15 +101,19 @@ export class FrigateCardLive extends LitElement {
|
||||
}
|
||||
|
||||
const fetchThumbnailsThenRender = async (): Promise<TemplateResult | void> => {
|
||||
if (!this.hass || !this.browseMediaQueryParameters) {
|
||||
if (!this.hass || !this.cameras || !this.view || !this.liveConfig) {
|
||||
return;
|
||||
}
|
||||
const browseMediaParams = BrowseMediaUtil.getBrowseMediaQueryParameters(
|
||||
this.liveConfig.controls.thumbnails.media,
|
||||
this.cameras.get(this.view.camera),
|
||||
);
|
||||
if (!browseMediaParams) {
|
||||
return;
|
||||
}
|
||||
let parent: BrowseMediaSource | null;
|
||||
try {
|
||||
parent = await BrowseMediaUtil.browseMediaQuery(
|
||||
this.hass,
|
||||
this.browseMediaQueryParameters,
|
||||
);
|
||||
parent = await BrowseMediaUtil.browseMediaQuery(this.hass, browseMediaParams);
|
||||
} catch (e) {
|
||||
return dispatchErrorMessageEvent(this, (e as Error).message);
|
||||
}
|
||||
@@ -113,10 +121,11 @@ export class FrigateCardLive extends LitElement {
|
||||
if (BrowseMediaUtil.getFirstTrueMediaChildIndex(parent) != null) {
|
||||
return html` <frigate-card-thumbnail-carousel
|
||||
.target=${parent}
|
||||
.view=${this.view}
|
||||
.config=${this.liveConfig?.controls.thumbnails}
|
||||
.highlightSelected=${false}
|
||||
@frigate-card:carousel:tap=${(ev: CustomEvent<ThumbnailCarouselTap>) => {
|
||||
const mediaType = this.browseMediaQueryParameters?.mediaType;
|
||||
const mediaType = browseMediaParams.mediaType;
|
||||
if (mediaType && this.view && ['snapshots', 'clips'].includes(mediaType)) {
|
||||
new View({
|
||||
view: mediaType === 'clips' ? 'clip-specific' : 'snapshot-specific',
|
||||
@@ -139,7 +148,7 @@ export class FrigateCardLive extends LitElement {
|
||||
* @returns A rendered template.
|
||||
*/
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this.hass || !this.liveConfig || !this.cameraConfig) {
|
||||
if (!this.hass || !this.liveConfig || !this.cameras) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -147,28 +156,19 @@ export class FrigateCardLive extends LitElement {
|
||||
${this.liveConfig.controls.thumbnails.mode === 'above'
|
||||
? this.renderThumbnails()
|
||||
: ''}
|
||||
${this.liveConfig.provider == 'frigate'
|
||||
? html` <frigate-card-live-frigate
|
||||
.hass=${this.hass}
|
||||
.cameraEntity=${this.cameraConfig.camera_entity}
|
||||
@frigate-card:media-show=${this._mediaShowHandler}
|
||||
>
|
||||
</frigate-card-live-frigate>`
|
||||
: this.liveConfig.provider == 'webrtc'
|
||||
? html`<frigate-card-live-webrtc
|
||||
.hass=${this.hass}
|
||||
.webRTCConfig=${this.liveConfig.webrtc || {}}
|
||||
@frigate-card:media-show=${this._mediaShowHandler}
|
||||
>
|
||||
</frigate-card-live-webrtc>`
|
||||
: html` <frigate-card-live-jsmpeg
|
||||
.hass=${this.hass}
|
||||
.cameraName=${this.cameraConfig.camera_name}
|
||||
.clientId=${this.cameraConfig.client_id}
|
||||
.jsmpegConfig=${this.liveConfig.jsmpeg}
|
||||
@frigate-card:media-show=${this._mediaShowHandler}
|
||||
>
|
||||
</frigate-card-live-jsmpeg>`}
|
||||
<frigate-card-live-carousel
|
||||
.hass=${this.hass}
|
||||
.view=${this.view}
|
||||
.cameras=${this.cameras}
|
||||
.liveConfig=${this.liveConfig}
|
||||
@frigate-card:media-show=${this._mediaShowHandler}
|
||||
@frigate-card:carousel:select=${() => {
|
||||
// Re-rendering the component will cause the thumbnails to be
|
||||
// re-fetched.
|
||||
this.requestUpdate();
|
||||
}}
|
||||
>
|
||||
</frigate-card-live-carousel>
|
||||
${this.liveConfig.controls.thumbnails.mode === 'below'
|
||||
? this.renderThumbnails()
|
||||
: ''}
|
||||
@@ -183,6 +183,183 @@ export class FrigateCardLive extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('frigate-card-live-carousel')
|
||||
export class FrigateCardLiveCarousel extends FrigateCardCarousel {
|
||||
@property({ attribute: false })
|
||||
protected hass?: HomeAssistant & ExtendedHomeAssistant;
|
||||
|
||||
@property({ attribute: false })
|
||||
protected view?: View;
|
||||
|
||||
@property({ attribute: false })
|
||||
protected cameras?: Map<string, CameraConfig>;
|
||||
|
||||
@property({ attribute: false })
|
||||
protected liveConfig?: LiveConfig;
|
||||
|
||||
/**
|
||||
* Get the Embla options to use.
|
||||
* @returns An EmblaOptionsType object or undefined for no options.
|
||||
*/
|
||||
protected _getOptions(): EmblaOptionsType {
|
||||
let startIndex = -1;
|
||||
if (this.cameras && this.view) {
|
||||
startIndex = Array.from(this.cameras.keys()).indexOf(this.view.camera);
|
||||
}
|
||||
|
||||
return {
|
||||
startIndex: startIndex < 0 ? undefined : startIndex,
|
||||
// TODO: draggable: this.viewerConfig.draggable,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the carousel.
|
||||
*/
|
||||
protected _loadCarousel(): void {
|
||||
super._loadCarousel();
|
||||
|
||||
// Necessary because typescript local type narrowing is not paying attention
|
||||
// to the side-effect of the call to super._loadCarousel().
|
||||
const carousel = this._carousel as EmblaCarouselType | undefined;
|
||||
carousel?.on('select', this._selectSlideSetViewHandler.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get slides to include in the render.
|
||||
* @returns The slides to include in the render.
|
||||
*/
|
||||
protected _getSlides(): TemplateResult[] {
|
||||
if (!this.cameras) {
|
||||
return [];
|
||||
}
|
||||
return Array.from(this.cameras.values()).map(this._renderLive.bind(this));
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Handle the user selecting a new slide in the carousel.
|
||||
// */
|
||||
protected _selectSlideSetViewHandler(): void {
|
||||
if (!this._carousel || !this.view || !this.cameras) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedSnap = this._carousel.selectedScrollSnap();
|
||||
this.view.camera = Array.from(this.cameras.keys())[selectedSnap];
|
||||
}
|
||||
|
||||
protected _renderLive(cameraConfig: CameraConfig): TemplateResult {
|
||||
// TODO: Add lazy load
|
||||
return html` <div class="embla__slide">
|
||||
<frigate-card-live-provider
|
||||
.hass=${this.hass}
|
||||
.cameraConfig=${cameraConfig}
|
||||
.liveConfig=${this.liveConfig}
|
||||
>
|
||||
</frigate-card-live-provider>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the element.
|
||||
* @returns A template to display to the user.
|
||||
*/
|
||||
protected render(): TemplateResult | void {
|
||||
const slides = this._getSlides();
|
||||
if (!slides) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ${neighbors && neighbors.previous
|
||||
// ? html`<frigate-card-next-previous-control
|
||||
// .direction=${'previous'}
|
||||
// .controlConfig=${this.viewerConfig?.controls.next_previous}
|
||||
// .thumbnail=${neighbors.previous.thumbnail}
|
||||
// .title=${neighbors.previous.title}
|
||||
// .actionHandler=${actionHandler({
|
||||
// hasHold: false,
|
||||
// hasDoubleClick: false,
|
||||
// })}
|
||||
// @action=${() => {
|
||||
// this._nextPreviousHandler('previous');
|
||||
// }}
|
||||
// ></frigate-card-next-previous-control>`
|
||||
// : ``}
|
||||
|
||||
return html`<div class="embla">
|
||||
<div class="embla__viewport">
|
||||
<div class="embla__container">${slides}</div>
|
||||
</div>
|
||||
</div>`;
|
||||
// ${neighbors && neighbors.next
|
||||
// ? html`<frigate-card-next-previous-control
|
||||
// .direction=${'next'}
|
||||
// .controlConfig=${this.viewerConfig?.controls.next_previous}
|
||||
// .thumbnail=${neighbors.next.thumbnail}
|
||||
// .title=${neighbors.next.title}
|
||||
// .actionHandler=${actionHandler({
|
||||
// hasHold: false,
|
||||
// hasDoubleClick: false,
|
||||
// })}
|
||||
// @action=${() => {
|
||||
// this._nextPreviousHandler('next');
|
||||
// }}
|
||||
// ></frigate-card-next-previous-control>`
|
||||
// : ``}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get element styles.
|
||||
*/
|
||||
static get styles(): CSSResultGroup {
|
||||
return [super.styles, unsafeCSS(mediaCarouselStyle)];
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('frigate-card-live-provider')
|
||||
export class FrigateCardLiveProvider extends LitElement {
|
||||
@property({ attribute: false })
|
||||
protected hass?: HomeAssistant & ExtendedHomeAssistant;
|
||||
|
||||
@property({ attribute: false })
|
||||
protected cameraConfig?: CameraConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
protected liveConfig?: LiveConfig;
|
||||
|
||||
/**
|
||||
* Master render method.
|
||||
* @returns A rendered template.
|
||||
*/
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this.hass || !this.liveConfig || !this.cameraConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
return html`
|
||||
${this.liveConfig.provider == 'frigate'
|
||||
? html` <frigate-card-live-frigate
|
||||
.hass=${this.hass}
|
||||
.cameraEntity=${this.cameraConfig.camera_entity}
|
||||
>
|
||||
</frigate-card-live-frigate>`
|
||||
: this.liveConfig.provider == 'webrtc'
|
||||
? html`<frigate-card-live-webrtc
|
||||
.hass=${this.hass}
|
||||
.webRTCConfig=${this.liveConfig.webrtc || {}}
|
||||
>
|
||||
</frigate-card-live-webrtc>`
|
||||
: html` <frigate-card-live-jsmpeg
|
||||
.hass=${this.hass}
|
||||
.cameraName=${this.cameraConfig.camera_name}
|
||||
.clientId=${this.cameraConfig.client_id}
|
||||
.jsmpegConfig=${this.liveConfig.jsmpeg}
|
||||
>
|
||||
</frigate-card-live-jsmpeg>`}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('frigate-card-live-frigate')
|
||||
export class FrigateCardLiveFrigate extends LitElement {
|
||||
@property({ attribute: false })
|
||||
@@ -478,7 +655,7 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
||||
return dispatchErrorMessageEvent(this, localize('error.jsmpeg_no_player'));
|
||||
}
|
||||
return html`${this._jsmpegCanvasElement}`;
|
||||
}
|
||||
};
|
||||
return html`${until(_render(), renderProgressIndicator())}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BrowseMediaUtil } from '../browse-media-util.js';
|
||||
import { CSSResultGroup, TemplateResult, html, unsafeCSS } from 'lit';
|
||||
import { EmblaOptionsType } from 'embla-carousel';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
|
||||
import type { BrowseMediaSource, ThumbnailsControlConfig } from '../types.js';
|
||||
@@ -38,9 +39,12 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
|
||||
this.style.setProperty('--frigate-card-carousel-thumbnail-opacity', value ? '0.6' : '1.0');
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._options = {
|
||||
/**
|
||||
* Get the Embla options to use.
|
||||
* @returns An EmblaOptionsType object or undefined for no options.
|
||||
*/
|
||||
protected _getOptions(): EmblaOptionsType {
|
||||
return {
|
||||
containScroll: 'keepSnaps',
|
||||
dragFree: true,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
||||
import { BrowseMediaUtil } from '../browse-media-util.js';
|
||||
import { EmblaCarouselType } from 'embla-carousel';
|
||||
import { EmblaCarouselType, EmblaOptionsType } from 'embla-carousel';
|
||||
import { HomeAssistant } from 'custom-card-helpers';
|
||||
import { createRef, Ref, ref } from 'lit/directives/ref.js';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
@@ -250,13 +250,10 @@ export class FrigateCardMediaCarousel extends FrigateCardCarousel {
|
||||
protected _slideHasBeenLazyLoaded: Record<number, boolean> = {};
|
||||
|
||||
/**
|
||||
* Load the carousel with "slides" (clips or snapshots).
|
||||
* Get the Embla options to use.
|
||||
* @returns An EmblaOptionsType object or undefined for no options.
|
||||
*/
|
||||
protected _loadCarousel(): void {
|
||||
if (this._carousel || !this.viewerConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
protected _getOptions(): EmblaOptionsType {
|
||||
// Start the carousel on the selected child number.
|
||||
const startIndex = Number(
|
||||
Object.keys(this._slideToChild).find(
|
||||
@@ -264,11 +261,16 @@ export class FrigateCardMediaCarousel extends FrigateCardCarousel {
|
||||
),
|
||||
);
|
||||
|
||||
this._options = {
|
||||
return {
|
||||
startIndex: isNaN(startIndex) ? undefined : startIndex,
|
||||
draggable: this.viewerConfig.draggable,
|
||||
draggable: this.viewerConfig?.draggable,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the carousel with "slides" (clips or snapshots).
|
||||
*/
|
||||
protected _loadCarousel(): void {
|
||||
super._loadCarousel();
|
||||
|
||||
// Necessary because typescript local type narrowing is not paying attention
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
frigate-card-live {
|
||||
frigate-card-live-carousel {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user