Fix carousel previous/next controls not showing.
This commit is contained in:
+10
-12
@@ -164,9 +164,10 @@ export class FrigateCardCarousel extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ReInit the carousel.
|
* ReInit the carousel but stay on the current slide.
|
||||||
*/
|
*/
|
||||||
protected _carouselReInit(options?: EmblaOptionsType): void {
|
protected _carouselReInitInPlaceInternal(): void {
|
||||||
|
const carouselReInit = (options?: EmblaOptionsType): void => {
|
||||||
// Allow the browser a moment to paint components that are inflight, to
|
// Allow the browser a moment to paint components that are inflight, to
|
||||||
// ensure accurate measurements are taken during the carousel
|
// ensure accurate measurements are taken during the carousel
|
||||||
// reinitialization.
|
// reinitialization.
|
||||||
@@ -174,13 +175,9 @@ export class FrigateCardCarousel extends LitElement {
|
|||||||
this._carousel?.reInit({ ...options });
|
this._carousel?.reInit({ ...options });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* ReInit the carousel but stay on the current slide.
|
|
||||||
*/
|
|
||||||
protected _carouselReInitInPlaceInternal(): void {
|
|
||||||
const selected = this.getCarouselSelected();
|
const selected = this.getCarouselSelected();
|
||||||
|
|
||||||
this._carouselReInit({
|
carouselReInit({
|
||||||
...(selected && { startIndex: selected.index }),
|
...(selected && { startIndex: selected.index }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -252,12 +249,11 @@ export class FrigateCardCarousel extends LitElement {
|
|||||||
axis: this.direction == 'horizontal' ? 'x' : 'y',
|
axis: this.direction == 'horizontal' ? 'x' : 'y',
|
||||||
speed: 20,
|
speed: 20,
|
||||||
...this.carouselOptions,
|
...this.carouselOptions,
|
||||||
...(this._savedStartIndex && { startIndex: this._savedStartIndex }),
|
...(this._savedStartIndex !== null && { startIndex: this._savedStartIndex }),
|
||||||
},
|
},
|
||||||
this.carouselPlugins,
|
this.carouselPlugins,
|
||||||
);
|
);
|
||||||
this._carousel.on('init', () => dispatchFrigateCardEvent(this, 'carousel:init'));
|
const selectSlide = (): void => {
|
||||||
this._carousel.on('select', () => {
|
|
||||||
const selected = this.getCarouselSelected();
|
const selected = this.getCarouselSelected();
|
||||||
if (selected) {
|
if (selected) {
|
||||||
dispatchFrigateCardEvent<CarouselSelect>(this, 'carousel:select', selected);
|
dispatchFrigateCardEvent<CarouselSelect>(this, 'carousel:select', selected);
|
||||||
@@ -266,8 +262,10 @@ export class FrigateCardCarousel extends LitElement {
|
|||||||
// Make sure every select causes a refresh to allow for re-paint of the
|
// Make sure every select causes a refresh to allow for re-paint of the
|
||||||
// next/previous controls.
|
// next/previous controls.
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
});
|
}
|
||||||
|
|
||||||
|
this._carousel.on('init', selectSlide);
|
||||||
|
this._carousel.on('select', selectSlide);
|
||||||
this._carousel.on('scroll', () => {
|
this._carousel.on('scroll', () => {
|
||||||
this._scrolling = true;
|
this._scrolling = true;
|
||||||
});
|
});
|
||||||
@@ -305,7 +303,7 @@ export class FrigateCardCarousel extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
const slides = this._refSlot.value?.assignedElements({ flatten: true }) || [];
|
const slides = this._refSlot.value?.assignedElements({ flatten: true }) || [];
|
||||||
const currentSlide = this._carousel?.selectedScrollSnap() ?? 0;
|
const currentSlide = (this._carousel?.selectedScrollSnap() ?? this.carouselOptions?.startIndex) ?? 0;
|
||||||
const showPrevious = this.carouselOptions?.loop || currentSlide > 0;
|
const showPrevious = this.carouselOptions?.loop || currentSlide > 0;
|
||||||
const showNext = this.carouselOptions?.loop || currentSlide + 1 < slides.length;
|
const showNext = this.carouselOptions?.loop || currentSlide + 1 < slides.length;
|
||||||
|
|
||||||
|
|||||||
@@ -155,7 +155,6 @@ export class FrigateCardMediaCarousel extends LitElement {
|
|||||||
// This carousel may be resized by Lovelace resizes, window resizes,
|
// This carousel may be resized by Lovelace resizes, window resizes,
|
||||||
// fullscreen, etc. Always call the adaptive height handler when the size
|
// fullscreen, etc. Always call the adaptive height handler when the size
|
||||||
// changes.
|
// changes.
|
||||||
protected _resizeObserver: ResizeObserver;
|
|
||||||
protected _slideResizeObserver: ResizeObserver;
|
protected _slideResizeObserver: ResizeObserver;
|
||||||
protected _intersectionObserver: IntersectionObserver;
|
protected _intersectionObserver: IntersectionObserver;
|
||||||
|
|
||||||
@@ -166,7 +165,6 @@ export class FrigateCardMediaCarousel extends LitElement {
|
|||||||
// Need to watch both changes in this element (e.g. caused by a window
|
// Need to watch both changes in this element (e.g. caused by a window
|
||||||
// resize or fullscreen change) and changes in the selected slide itself
|
// resize or fullscreen change) and changes in the selected slide itself
|
||||||
// (e.g. changing from a progress indicator to a loaded media).
|
// (e.g. changing from a progress indicator to a loaded media).
|
||||||
this._resizeObserver = new ResizeObserver(this._reInitAndAdjustHeight.bind(this));
|
|
||||||
this._slideResizeObserver = new ResizeObserver(
|
this._slideResizeObserver = new ResizeObserver(
|
||||||
this._reInitAndAdjustHeight.bind(this),
|
this._reInitAndAdjustHeight.bind(this),
|
||||||
);
|
);
|
||||||
@@ -294,7 +292,6 @@ export class FrigateCardMediaCarousel extends LitElement {
|
|||||||
this._debouncedAdaptContainerHeightToSlide,
|
this._debouncedAdaptContainerHeightToSlide,
|
||||||
);
|
);
|
||||||
this.removeEventListener('frigate-card:media:loaded', this._boundTitleHandler);
|
this.removeEventListener('frigate-card:media:loaded', this._boundTitleHandler);
|
||||||
this._resizeObserver.disconnect();
|
|
||||||
this._intersectionObserver.disconnect();
|
this._intersectionObserver.disconnect();
|
||||||
|
|
||||||
this._mediaLoadedInfo = {};
|
this._mediaLoadedInfo = {};
|
||||||
@@ -397,15 +394,14 @@ export class FrigateCardMediaCarousel extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
const selectSlide = (ev?: CustomEvent<CarouselSelect>): void => {
|
const selectSlide = (ev: CustomEvent<CarouselSelect>): void => {
|
||||||
this._slideResizeObserver.disconnect();
|
this._slideResizeObserver.disconnect();
|
||||||
const parent = this.getRootNode();
|
const parent = this.getRootNode();
|
||||||
if (parent && parent instanceof ShadowRoot) {
|
if (parent && parent instanceof ShadowRoot) {
|
||||||
this._slideResizeObserver.observe(parent.host);
|
this._slideResizeObserver.observe(parent.host);
|
||||||
}
|
}
|
||||||
|
|
||||||
const selected = ev ? ev.detail : this.frigateCardCarousel()?.getCarouselSelected();
|
const selected = ev.detail;
|
||||||
if (selected) {
|
|
||||||
this._slideResizeObserver.observe(selected.element);
|
this._slideResizeObserver.observe(selected.element);
|
||||||
|
|
||||||
// Pass up the media-carousel select event first to allow parents to
|
// Pass up the media-carousel select event first to allow parents to
|
||||||
@@ -419,16 +415,12 @@ export class FrigateCardMediaCarousel extends LitElement {
|
|||||||
// Dispatch media info.
|
// Dispatch media info.
|
||||||
this._dispatchMediaLoadedInfo(selected);
|
this._dispatchMediaLoadedInfo(selected);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return html` <frigate-card-carousel
|
return html` <frigate-card-carousel
|
||||||
${ref(this._refCarousel)}
|
${ref(this._refCarousel)}
|
||||||
.carouselOptions=${this.carouselOptions}
|
.carouselOptions=${this.carouselOptions}
|
||||||
.carouselPlugins=${this.carouselPlugins}
|
.carouselPlugins=${this.carouselPlugins}
|
||||||
transitionEffect=${ifDefined(this.transitionEffect)}
|
transitionEffect=${ifDefined(this.transitionEffect)}
|
||||||
@frigate-card:carousel:init=${() => {
|
|
||||||
selectSlide();
|
|
||||||
}}
|
|
||||||
@frigate-card:carousel:select=${(ev: CustomEvent<CarouselSelect>) => {
|
@frigate-card:carousel:select=${(ev: CustomEvent<CarouselSelect>) => {
|
||||||
selectSlide(ev);
|
selectSlide(ev);
|
||||||
}}
|
}}
|
||||||
|
|||||||
+37
-29
@@ -9,7 +9,6 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
unsafeCSS,
|
unsafeCSS,
|
||||||
} from 'lit';
|
} from 'lit';
|
||||||
import { guard } from 'lit/directives/guard.js';
|
|
||||||
import { customElement, property } from 'lit/decorators.js';
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||||
import { createRef, Ref, ref } from 'lit/directives/ref.js';
|
import { createRef, Ref, ref } from 'lit/directives/ref.js';
|
||||||
@@ -49,11 +48,11 @@ import {
|
|||||||
wrapRawMediaLoadedEventForCarousel,
|
wrapRawMediaLoadedEventForCarousel,
|
||||||
wrapMediaLoadedEventForCarousel,
|
wrapMediaLoadedEventForCarousel,
|
||||||
} from './media-carousel.js';
|
} from './media-carousel.js';
|
||||||
|
import type { CarouselSelect } from './carousel.js';
|
||||||
import './next-prev-control.js';
|
import './next-prev-control.js';
|
||||||
import './title-control.js';
|
import './title-control.js';
|
||||||
import '../patches/ha-hls-player';
|
import '../patches/ha-hls-player';
|
||||||
import './surround.js';
|
import './surround.js';
|
||||||
import { EmblaCarouselPlugins } from './carousel.js';
|
|
||||||
import { renderTask } from '../utils/task.js';
|
import { renderTask } from '../utils/task.js';
|
||||||
import { updateElementStyleFromMediaLayoutConfig } from '../utils/media-layout.js';
|
import { updateElementStyleFromMediaLayoutConfig } from '../utils/media-layout.js';
|
||||||
import { DataManager } from '../utils/data-manager.js';
|
import { DataManager } from '../utils/data-manager.js';
|
||||||
@@ -220,6 +219,9 @@ export class FrigateCardViewerCarousel extends LitElement {
|
|||||||
// (Folders are not media items that can be rendered).
|
// (Folders are not media items that can be rendered).
|
||||||
protected _slideToChild: Record<number, number> = {};
|
protected _slideToChild: Record<number, number> = {};
|
||||||
|
|
||||||
|
protected _carouselOptions?: EmblaOptionsType;
|
||||||
|
protected _carouselPlugins?: EmblaPluginType[];
|
||||||
|
|
||||||
// A task to resolve target media if lazy loading is disabled.
|
// A task to resolve target media if lazy loading is disabled.
|
||||||
protected _mediaResolutionTask = new Task<
|
protected _mediaResolutionTask = new Task<
|
||||||
[FrigateBrowseMediaSource | null | undefined],
|
[FrigateBrowseMediaSource | null | undefined],
|
||||||
@@ -516,17 +518,13 @@ export class FrigateCardViewerCarousel extends LitElement {
|
|||||||
/**
|
/**
|
||||||
* Handle the user selecting a new slide in the carousel.
|
* Handle the user selecting a new slide in the carousel.
|
||||||
*/
|
*/
|
||||||
protected _setViewHandler(): void {
|
protected _setViewHandler(ev: CustomEvent<CarouselSelect>): void {
|
||||||
if (!this._refMediaCarousel.value || !this.view) {
|
if (!this._refMediaCarousel.value || !this.view) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the childIndex in the view.
|
// Update the childIndex in the view.
|
||||||
const selected = this._refMediaCarousel.value
|
const childIndex = this._slideToChild[ev.detail.index];
|
||||||
.frigateCardCarousel()
|
|
||||||
?.getCarouselSelected()?.index;
|
|
||||||
if (selected !== undefined) {
|
|
||||||
const childIndex = this._slideToChild[selected];
|
|
||||||
if (childIndex !== undefined) {
|
if (childIndex !== undefined) {
|
||||||
this.view
|
this.view
|
||||||
.evolve({
|
.evolve({
|
||||||
@@ -535,7 +533,6 @@ export class FrigateCardViewerCarousel extends LitElement {
|
|||||||
.dispatchChangeEvent(this);
|
.dispatchChangeEvent(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure media URLs use the correct HA URL (relevant for Chromecast where the
|
* Ensure media URLs use the correct HA URL (relevant for Chromecast where the
|
||||||
@@ -596,30 +593,27 @@ export class FrigateCardViewerCarousel extends LitElement {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get slides to include in the render.
|
* Get slides to include in the render.
|
||||||
* @returns The slides to include in the render and an index keyed by slide
|
* @returns The slides to include in the render.
|
||||||
* number that maps to child number.
|
|
||||||
*/
|
*/
|
||||||
protected _getSlides(): [TemplateResult[], Record<number, number>] {
|
protected _getSlides(): TemplateResult[] {
|
||||||
if (
|
if (
|
||||||
!this.view ||
|
!this.view ||
|
||||||
!this.view.target ||
|
!this.view.target ||
|
||||||
!this.view.target.children ||
|
!this.view.target.children ||
|
||||||
!this.view.target.children.length
|
!this.view.target.children.length
|
||||||
) {
|
) {
|
||||||
return [[], {}];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const slideToChild: Record<number, number> = {};
|
|
||||||
const slides: TemplateResult[] = [];
|
const slides: TemplateResult[] = [];
|
||||||
for (let i = 0; i < this.view.target.children?.length; ++i) {
|
for (let i = 0; i < this.view.target.children?.length; ++i) {
|
||||||
const slide = this._renderMediaItem(this.view.target.children[i], slides.length);
|
const slide = this._renderMediaItem(this.view.target.children[i], slides.length);
|
||||||
|
|
||||||
if (slide) {
|
if (slide) {
|
||||||
slideToChild[slides.length] = i;
|
|
||||||
slides.push(slide);
|
slides.push(slide);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [slides, slideToChild];
|
return slides;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -639,17 +633,38 @@ export class FrigateCardViewerCarousel extends LitElement {
|
|||||||
* @param changedProps The changed properties
|
* @param changedProps The changed properties
|
||||||
*/
|
*/
|
||||||
protected willUpdate(changedProps: PropertyValues): void {
|
protected willUpdate(changedProps: PropertyValues): void {
|
||||||
|
// Pre-populate a map between real media slides and view child indicies.
|
||||||
|
if (changedProps.has('view')) {
|
||||||
|
this._slideToChild = {};
|
||||||
|
let i = 0;
|
||||||
|
(this.view?.target?.children ?? []).forEach((child, index) => {
|
||||||
|
if (isTrueMedia(child) && ['video', 'image'].includes(child.media_content_type)) {
|
||||||
|
this._slideToChild[i++] = index;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (changedProps.has('viewerConfig')) {
|
if (changedProps.has('viewerConfig')) {
|
||||||
updateElementStyleFromMediaLayoutConfig(this, this.viewerConfig?.layout);
|
updateElementStyleFromMediaLayoutConfig(this, this.viewerConfig?.layout);
|
||||||
}
|
}
|
||||||
|
if (!this._carouselOptions || changedProps.has('viewerConfig')) {
|
||||||
|
this._carouselOptions = this._getOptions();
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!this._carouselPlugins ||
|
||||||
|
changedProps.has('viewerConfig') ||
|
||||||
|
(changedProps.has('view') &&
|
||||||
|
this.view?.target?.children?.length !==
|
||||||
|
changedProps.get('view')?.target?.children?.length)
|
||||||
|
) {
|
||||||
|
this._carouselPlugins = this._getPlugins();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the element, resolving the media first if necessary.
|
* Render the element, resolving the media first if necessary.
|
||||||
*/
|
*/
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
this._slideToChild = {};
|
|
||||||
|
|
||||||
// If lazy loading is not enabled, wait for the media resolver task to
|
// If lazy loading is not enabled, wait for the media resolver task to
|
||||||
// complete and show a progress indictator until this.
|
// complete and show a progress indictator until this.
|
||||||
if (!this.viewerConfig?.lazy_load && !this._isMediaFullyResolved()) {
|
if (!this.viewerConfig?.lazy_load && !this._isMediaFullyResolved()) {
|
||||||
@@ -665,8 +680,8 @@ export class FrigateCardViewerCarousel extends LitElement {
|
|||||||
* @returns A template to display to the user.
|
* @returns A template to display to the user.
|
||||||
*/
|
*/
|
||||||
protected _render(): TemplateResult | void {
|
protected _render(): TemplateResult | void {
|
||||||
const [slides, slideToChild] = this._getSlides();
|
const slides = this._getSlides();
|
||||||
this._slideToChild = slideToChild;
|
|
||||||
if (!slides.length || !this.view?.media) {
|
if (!slides.length || !this.view?.media) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -674,17 +689,10 @@ export class FrigateCardViewerCarousel extends LitElement {
|
|||||||
const neighbors = this._getMediaNeighbors();
|
const neighbors = this._getMediaNeighbors();
|
||||||
const [prev, next] = [neighbors?.previous, neighbors?.next];
|
const [prev, next] = [neighbors?.previous, neighbors?.next];
|
||||||
|
|
||||||
// Notes on the below:
|
|
||||||
// - guard() is used to avoid reseting the carousel unless the
|
|
||||||
// options/plugins actually change.
|
|
||||||
|
|
||||||
return html` <frigate-card-media-carousel
|
return html` <frigate-card-media-carousel
|
||||||
${ref(this._refMediaCarousel)}
|
${ref(this._refMediaCarousel)}
|
||||||
.carouselOptions=${guard([this.viewerConfig], this._getOptions.bind(this))}
|
.carouselOptions=${this._carouselOptions}
|
||||||
.carouselPlugins=${guard(
|
.carouselPlugins=${this._carouselPlugins}
|
||||||
[this.viewerConfig, this.view?.target?.children?.length],
|
|
||||||
this._getPlugins.bind(this),
|
|
||||||
) as EmblaCarouselPlugins}
|
|
||||||
.label="${this.view.media.title}"
|
.label="${this.view.media.title}"
|
||||||
.titlePopupConfig=${this.viewerConfig?.controls.title}
|
.titlePopupConfig=${this.viewerConfig?.controls.title}
|
||||||
transitionEffect=${this._getTransitionEffect()}
|
transitionEffect=${this._getTransitionEffect()}
|
||||||
|
|||||||
Reference in New Issue
Block a user