Avoid carousel init duplication.

This commit is contained in:
Dermot Duffy
2023-08-13 10:00:29 -07:00
parent c61ceb905d
commit b4595867b3
4 changed files with 42 additions and 16 deletions
+19 -7
View File
@@ -11,10 +11,11 @@ import {
} from 'lit'; } from 'lit';
import { customElement, property } from 'lit/decorators.js'; import { customElement, property } from 'lit/decorators.js';
import { Ref, createRef, ref } from 'lit/directives/ref.js'; import { Ref, createRef, ref } from 'lit/directives/ref.js';
import isEqual from 'lodash-es/isEqual';
import throttle from 'lodash-es/throttle'; import throttle from 'lodash-es/throttle';
import carouselStyle from '../scss/carousel.scss'; import carouselStyle from '../scss/carousel.scss';
import { TransitionEffect } from '../types'; import { TransitionEffect } from '../types';
import { dispatchFrigateCardEvent } from '../utils/basic.js'; import { dispatchFrigateCardEvent, isHTMLElement } from '../utils/basic.js';
export interface CarouselSelect { export interface CarouselSelect {
index: number; index: number;
@@ -175,6 +176,13 @@ export class FrigateCardCarousel extends LitElement {
this._carousel = undefined; this._carousel = undefined;
} }
protected _getSlideElements(): HTMLElement[] {
return (
this._refSlot.value?.assignedElements({ flatten: true }).filter(isHTMLElement) ??
[]
);
}
/** /**
* Initialize the carousel. * Initialize the carousel.
*/ */
@@ -187,7 +195,7 @@ export class FrigateCardCarousel extends LitElement {
root: carouselNode, root: carouselNode,
// As the slides are slotted, need to explicitly pull them out and pass // As the slides are slotted, need to explicitly pull them out and pass
// them to Embla. // them to Embla.
slides: this._refSlot.value?.assignedElements({ flatten: true }) as HTMLElement[], slides: this._getSlideElements(),
}; };
if (carouselNode && nodes.slides) { if (carouselNode && nodes.slides) {
@@ -219,7 +227,7 @@ export class FrigateCardCarousel extends LitElement {
// the selected slide as returned by the carousel. This need should be // the selected slide as returned by the carousel. This need should be
// verified in future versions of Embla (tested as necessary on v7.0.9). // verified in future versions of Embla (tested as necessary on v7.0.9).
// Test case: // Test case:
// //
// - Start in `live` view in grid mode. // - Start in `live` view in grid mode.
// - Select any camera that is not the first one. // - Select any camera that is not the first one.
// - Go to non-grid mode. // - Go to non-grid mode.
@@ -256,10 +264,14 @@ export class FrigateCardCarousel extends LitElement {
* Called when the slotted children in the carousel change. * Called when the slotted children in the carousel change.
*/ */
protected _slotChanged(): void { protected _slotChanged(): void {
// Cannot just re-init, because the slide elements themselves may have // Check whether the slotted elements have changed (without this check the
// changed, and only a carousel init can pass in new (slotted) children. If // carousel initializations are duplicated).
this._destroyCarousel(); if (!isEqual(this._getSlideElements(), this._carousel?.slideNodes())) {
this.requestUpdate(); // Cannot just re-init, because the slide elements themselves may have
// changed, and only a carousel init can pass in new (slotted) children.
this._destroyCarousel();
this.requestUpdate();
}
} }
protected render(): TemplateResult | void { protected render(): TemplateResult | void {
-4
View File
@@ -1,8 +1,4 @@
// TODO: Performance of video scanning (pause/play?) // TODO: Performance of video scanning (pause/play?)
// TODO: Investigate query spam during a grid load
// TODO: Do I need column max?
// TODO: frigate-card-live-go2rtc.hidden double media load event.
// TODO: Why am I creating so many carousels in the race-condition issue? Expecting 5, getting 10.
import { import {
CSSResultGroup, CSSResultGroup,
+7 -1
View File
@@ -225,4 +225,10 @@ export const setOrRemoveAttribute = (
/** /**
* Allow typescript to narrow types based on truthy filter. * Allow typescript to narrow types based on truthy filter.
*/ */
export const filterTruthy = <T>(x: T | false | undefined | null | '' | 0): x is T => !!x; export const isTruthy = <T>(x: T | false | undefined | null | '' | 0): x is T => !!x;
/**
* Allow typescript to narrow types for HTMLElements.
*/
export const isHTMLElement = (element: unknown): element is HTMLElement =>
element instanceof HTMLElement;
+16 -4
View File
@@ -8,7 +8,7 @@ import {
dayToDate, dayToDate,
dispatchFrigateCardEvent, dispatchFrigateCardEvent,
errorToConsole, errorToConsole,
filterTruthy, isTruthy,
formatDate, formatDate,
formatDateAndTime, formatDateAndTime,
getDurationString, getDurationString,
@@ -20,6 +20,7 @@ import {
setify, setify,
setOrRemoveAttribute, setOrRemoveAttribute,
sleep, sleep,
isHTMLElement,
} from '../../src/utils/basic'; } from '../../src/utils/basic';
// @vitest-environment jsdom // @vitest-environment jsdom
@@ -248,11 +249,22 @@ describe('setOrRemoveAttribute', () => {
}); });
}); });
describe('filterTruthy', () => { describe('isTruthy', () => {
it('should return true for true', () => { it('should return true for true', () => {
expect(filterTruthy(true)).toBeTruthy(); expect(isTruthy(true)).toBeTruthy();
}); });
it('should return false for false', () => { it('should return false for false', () => {
expect(filterTruthy(false)).toBeFalsy(); expect(isTruthy(false)).toBeFalsy();
});
});
describe('isHTMLElement', () => {
it('should return true for HTMLElement', () => {
const htmlElement = document.createElement('div');
expect(isHTMLElement(htmlElement)).toBeTruthy();
});
it('should return false for Element', () => {
const svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
expect(isHTMLElement(svgElement)).toBeFalsy();
}); });
}); });