diff --git a/src/components/drawer.ts b/src/components/drawer.ts index 2945b3f3..7863080c 100644 --- a/src/components/drawer.ts +++ b/src/components/drawer.ts @@ -24,9 +24,18 @@ export class FrigateCardDrawer extends LitElement { @property({ type: Boolean, reflect: true, attribute: true }) public open = false; + // The 'empty' attribute is used in the styling to change the drawer + // visibility and that of all descendants if there is no content. Styling is + // used rather than display or hidden in order to ensure the contents continue + // to have a measurable size. + @property({ type: Boolean, reflect: true, attribute: true }) + public empty = true; + protected _refDrawer: Ref = createRef(); protected _refSlot: Ref = createRef(); + protected _resizeObserver = new ResizeObserver(() => this._hideDrawerIfNecessary()); + /** * Called on the first update. * @param changedProps The changed properties. @@ -42,19 +51,45 @@ export class FrigateCardDrawer extends LitElement { this._refDrawer.value?.shadowRoot?.appendChild(style); } + /** + * Called when the slotted children in the drawer change. + */ protected _slotChanged(): void { const elements = this._refSlot.value?.assignedElements({ flatten: true }); - if (elements && elements.length && this._refDrawer.value) { - // Hide the drawer unless there is content. - this._refDrawer.value.hidden = false; + + // Watch all slot children for size changes. + this._resizeObserver.disconnect(); + for (const element of elements ?? []) { + this._resizeObserver.observe(element); } + this._hideDrawerIfNecessary(); + } + + /** + * Hide the drawer if there is nothing to show. + * @returns + */ + protected _hideDrawerIfNecessary(): void { + if (!this._refDrawer.value) { + return; + } + + const elements = this._refSlot.value?.assignedElements({ flatten: true }); + this.empty = + !elements || + !elements.length || + // If the element has the special attribute 'empty' also hide it, this is + // used to hide carousels that have no actual contents. + elements.every((element) => { + const box = element.getBoundingClientRect(); + return !box.width || !box.height; + }); } protected render(): TemplateResult { return html` diff --git a/src/components/live.ts b/src/components/live.ts index b36e2a18..7c1d52ce 100644 --- a/src/components/live.ts +++ b/src/components/live.ts @@ -512,7 +512,7 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel { protected render(): TemplateResult | void { const [slides, cameraToSlide] = this._getSlides(); this._cameraToSlide = cameraToSlide; - if (!slides || !this.liveConfig || !this.cameras || !this.view) { + if (!slides.length || !this.liveConfig || !this.cameras || !this.view) { return; } diff --git a/src/components/thumbnail-carousel.ts b/src/components/thumbnail-carousel.ts index ae05d5de..bfa0b36b 100644 --- a/src/components/thumbnail-carousel.ts +++ b/src/components/thumbnail-carousel.ts @@ -5,10 +5,7 @@ import { classMap } from 'lit/directives/class-map.js'; import { customElement, property, state } from 'lit/decorators.js'; import { ifDefined } from 'lit/directives/if-defined.js'; -import type { - FrigateBrowseMediaSource, - ThumbnailsControlConfig, -} from '../types.js'; +import type { FrigateBrowseMediaSource, ThumbnailsControlConfig } from '../types.js'; import { FrigateCardCarousel } from './carousel.js'; import { View } from '../view.js'; import { @@ -201,7 +198,7 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel { */ protected render(): TemplateResult | void { const slides = this._getSlides(); - if (!slides || !this._config || this._config.mode == 'none') { + if (!slides.length || !this._config || this._config.mode == 'none') { return; } diff --git a/src/components/viewer.ts b/src/components/viewer.ts index 28ec2abc..2f4c1dcd 100644 --- a/src/components/viewer.ts +++ b/src/components/viewer.ts @@ -599,7 +599,7 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel { protected _render(): TemplateResult | void { const [slides, slideToChild] = this._getSlides(); this._slideToChild = slideToChild; - if (!slides) { + if (!slides.length) { return; } diff --git a/src/scss/drawer-inject.scss b/src/scss/drawer-inject.scss index 830ca6a8..4f82950d 100644 --- a/src/scss/drawer-inject.scss +++ b/src/scss/drawer-inject.scss @@ -22,7 +22,6 @@ #d { // Need to allow drawer controls to be visible. overflow: visible; - visibility: visible; } :host([location=right]) #d { diff --git a/src/scss/drawer.scss b/src/scss/drawer.scss index d69570af..6c5288cf 100644 --- a/src/scss/drawer.scss +++ b/src/scss/drawer.scss @@ -27,6 +27,12 @@ div.control-surround { } right: 100%; } +:host([empty]), :host([empty]) > * { + visibility: hidden; +} +:host(:not([empty])), :host(:not([empty])) > * { + visibility: visible; +} ha-icon.control { color: var(--secondary-color, white);