Ensure container resizes when parent resizes.
This commit is contained in:
@@ -22,6 +22,7 @@ import './next-prev-control.js';
|
||||
import './carousel.js';
|
||||
import { FrigateCardNextPreviousControl } from './next-prev-control.js';
|
||||
import { FrigateCardTitleControl } from './title-control.js';
|
||||
import { debounce } from 'lodash-es';
|
||||
|
||||
const getEmptyImageSrc = (width: number, height: number) =>
|
||||
`data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}"%3E%3C/svg%3E`;
|
||||
@@ -143,10 +144,14 @@ export class FrigateCardMediaCarousel extends LitElement {
|
||||
|
||||
protected _boundAutoPlayHandler = this.autoPlay.bind(this);
|
||||
protected _boundAutoUnmuteHandler = this.autoUnmute.bind(this);
|
||||
protected _boundAdaptContainerHeightToSlide =
|
||||
this._adaptContainerHeightToSlide.bind(this);
|
||||
protected _boundTitleHandler = this._titleHandler.bind(this);
|
||||
|
||||
// Debounce multiple calls to adapt the container height.
|
||||
protected _debouncedAdaptContainerHeightToSlide = debounce(
|
||||
this._adaptContainerHeightToSlide.bind(this),
|
||||
1 * 100,
|
||||
{trailing: true});
|
||||
|
||||
// This carousel may be resized by Lovelace resizes, window resizes,
|
||||
// fullscreen, etc. Always call the adaptive height handler when the size
|
||||
// changes.
|
||||
@@ -272,10 +277,9 @@ export class FrigateCardMediaCarousel extends LitElement {
|
||||
this.addEventListener('frigate-card:media:loaded', this._boundAutoUnmuteHandler);
|
||||
this.addEventListener(
|
||||
'frigate-card:media:loaded',
|
||||
this._boundAdaptContainerHeightToSlide,
|
||||
this._debouncedAdaptContainerHeightToSlide,
|
||||
);
|
||||
this.addEventListener('frigate-card:media:loaded', this._boundTitleHandler);
|
||||
this._resizeObserver.observe(this);
|
||||
this._intersectionObserver.observe(this);
|
||||
}
|
||||
|
||||
@@ -287,7 +291,7 @@ export class FrigateCardMediaCarousel extends LitElement {
|
||||
this.removeEventListener('frigate-card:media:loaded', this._boundAutoUnmuteHandler);
|
||||
this.removeEventListener(
|
||||
'frigate-card:media:loaded',
|
||||
this._boundAdaptContainerHeightToSlide,
|
||||
this._debouncedAdaptContainerHeightToSlide,
|
||||
);
|
||||
this.removeEventListener('frigate-card:media:loaded', this._boundTitleHandler);
|
||||
this._resizeObserver.disconnect();
|
||||
@@ -302,7 +306,7 @@ export class FrigateCardMediaCarousel extends LitElement {
|
||||
*/
|
||||
protected _reInitAndAdjustHeight(): void {
|
||||
this.frigateCardCarousel()?.carouselReInitWhenSafe();
|
||||
this._adaptContainerHeightToSlide();
|
||||
this._debouncedAdaptContainerHeightToSlide();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -331,36 +335,25 @@ export class FrigateCardMediaCarousel extends LitElement {
|
||||
* actually the media load/show that will change the dimensions, and that is
|
||||
* async from carousel actions (e.g. lazy-loaded media).
|
||||
*
|
||||
* This component does not use the stock Embla auto-height plugin as it
|
||||
* resizes the container on selection rather than media load.
|
||||
* This component does not use the stock Embla auto-height plugin as that
|
||||
* resizes the container only on selection rather than media load.
|
||||
*/
|
||||
protected _adaptContainerHeightToSlide(): void {
|
||||
const adaptCarouselHeight = (): void => {
|
||||
const selected = this.frigateCardCarousel()?.getCarouselSelected();
|
||||
if (selected) {
|
||||
this.style.removeProperty('max-height');
|
||||
const height = selected.element.getBoundingClientRect().height;
|
||||
if (height !== undefined && height > 0) {
|
||||
this.style.maxHeight = `${height}px`;
|
||||
}
|
||||
const selected = this.frigateCardCarousel()?.getCarouselSelected();
|
||||
if (selected) {
|
||||
this.style.removeProperty('max-height');
|
||||
const height = selected.element.getBoundingClientRect().height;
|
||||
if (height !== undefined && height > 0) {
|
||||
this.style.maxHeight = `${height}px`;
|
||||
}
|
||||
};
|
||||
|
||||
// Hack: This method attempts to measure the height of the selected slide in
|
||||
// order to set the overall carousel height to match. This method is
|
||||
// triggered from `frigate-card:media:loaded` events, which are usually in
|
||||
// turn triggered from media/metadata load events from media players.
|
||||
// Sufficient time needs to be allowed after these metadata load events to
|
||||
// allow the browser to repaint the element heights, so that we can get the
|
||||
// right values here. requestAnimationFrame() works well for this.
|
||||
window.requestAnimationFrame(adaptCarouselHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire a media show event when a slide is selected.
|
||||
*/
|
||||
protected _dispatchMediaLoadedInfo(): void {
|
||||
const slideIndex = this.frigateCardCarousel()?.getCarouselSelected()?.index;
|
||||
protected _dispatchMediaLoadedInfo(selected: CarouselSelect): void {
|
||||
const slideIndex = selected.index;
|
||||
if (slideIndex !== undefined && slideIndex in this._mediaLoadedInfo) {
|
||||
dispatchExistingMediaLoadedInfoAsEvent(this, this._mediaLoadedInfo[slideIndex]);
|
||||
}
|
||||
@@ -404,26 +397,40 @@ export class FrigateCardMediaCarousel extends LitElement {
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
const selectSlide = (ev?: CustomEvent<CarouselSelect>): void => {
|
||||
this._slideResizeObserver.disconnect();
|
||||
const parent = this.getRootNode();
|
||||
if (parent && parent instanceof ShadowRoot) {
|
||||
this._slideResizeObserver.observe(parent.host);
|
||||
}
|
||||
|
||||
const selected = ev ? ev.detail : this.frigateCardCarousel()?.getCarouselSelected();
|
||||
if (selected) {
|
||||
this._slideResizeObserver.observe(selected.element);
|
||||
|
||||
// Pass up the media-carousel select event first to allow parents to
|
||||
// initialize/reset before the media info is dispatched.
|
||||
dispatchFrigateCardEvent<CarouselSelect>(
|
||||
this,
|
||||
'media-carousel:select',
|
||||
selected,
|
||||
);
|
||||
|
||||
// Dispatch media info.
|
||||
this._dispatchMediaLoadedInfo(selected);
|
||||
}
|
||||
}
|
||||
|
||||
return html` <frigate-card-carousel
|
||||
${ref(this._refCarousel)}
|
||||
.carouselOptions=${this.carouselOptions}
|
||||
.carouselPlugins=${this.carouselPlugins}
|
||||
transitionEffect=${ifDefined(this.transitionEffect)}
|
||||
@frigate-card:carousel:init=${this._dispatchMediaLoadedInfo.bind(this)}
|
||||
@frigate-card:carousel:init=${() => {
|
||||
selectSlide();
|
||||
}}
|
||||
@frigate-card:carousel:select=${(ev: CustomEvent<CarouselSelect>) => {
|
||||
this._slideResizeObserver.disconnect();
|
||||
this._slideResizeObserver.observe(ev.detail.element);
|
||||
|
||||
// Pass up the media-carousel select event first to allow parents to
|
||||
// initialize/reset before the media info is dispatched.
|
||||
dispatchFrigateCardEvent<CarouselSelect>(
|
||||
this,
|
||||
'media-carousel:select',
|
||||
ev.detail,
|
||||
);
|
||||
|
||||
// Dispatch media info.
|
||||
this._dispatchMediaLoadedInfo();
|
||||
selectSlide(ev);
|
||||
}}
|
||||
@frigate-card:carousel:media:loaded=${this._storeMediaLoadedInfo.bind(this)}
|
||||
@frigate-card:carousel:media:unloaded=${this._removeMediaLoadedInfo.bind(this)}
|
||||
|
||||
@@ -328,7 +328,9 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
if (!onlyShowMatchingHour || isMatchingHour) {
|
||||
children.push(
|
||||
createChild(
|
||||
`${prettifyTitle(config.frigate.camera_name)} ${formatDateAndTime(hour)}`,
|
||||
`${prettifyTitle(config.frigate.camera_name)} ${formatDateAndTime(
|
||||
hour,
|
||||
)}`,
|
||||
getRecordingMediaContentID({
|
||||
clientId: config.frigate.client_id,
|
||||
year: dayData.day.getFullYear(),
|
||||
@@ -466,7 +468,6 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
properties.event.additionalEvent !== 'pinchin' &&
|
||||
properties.event.additionalEvent !== 'pinchout'
|
||||
) {
|
||||
|
||||
const targetTime = this._pointerHeld?.window
|
||||
? add(properties.start, {
|
||||
seconds:
|
||||
@@ -683,7 +684,8 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
if (properties.group) {
|
||||
this._changeViewToRecording(
|
||||
properties.what === 'background' ? properties.time : window.end,
|
||||
String(properties.group));
|
||||
String(properties.group),
|
||||
);
|
||||
} else if (this.mini && this.view?.camera) {
|
||||
// In mini mode group may not be displayed / used, so just use the camera directly.
|
||||
this._changeViewToRecording(window.end, this.view.camera);
|
||||
@@ -816,48 +818,50 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
: this._timeline.getSelection();
|
||||
let childIndex = -1;
|
||||
const children: FrigateBrowseMediaSource[] = [];
|
||||
this._dataview?.get({
|
||||
filter: (item) => item.type !== 'background',
|
||||
order: sortTimelineItemsYoungestToOldest }
|
||||
).forEach((item) => {
|
||||
const cameraID = item.group ? String(item.group) : null;
|
||||
const cameraConfig = cameraID ? this.cameras?.get(cameraID) : null;
|
||||
const event = item.event;
|
||||
const media =
|
||||
event?.has_clip && this.timelineConfig?.media !== 'snapshots'
|
||||
? 'clips'
|
||||
: event?.has_snapshot
|
||||
? 'snapshots'
|
||||
: null;
|
||||
this._dataview
|
||||
?.get({
|
||||
filter: (item) => item.type !== 'background',
|
||||
order: sortTimelineItemsYoungestToOldest,
|
||||
})
|
||||
.forEach((item) => {
|
||||
const cameraID = item.group ? String(item.group) : null;
|
||||
const cameraConfig = cameraID ? this.cameras?.get(cameraID) : null;
|
||||
const event = item.event;
|
||||
const media =
|
||||
event?.has_clip && this.timelineConfig?.media !== 'snapshots'
|
||||
? 'clips'
|
||||
: event?.has_snapshot
|
||||
? 'snapshots'
|
||||
: null;
|
||||
|
||||
if (
|
||||
cameraID &&
|
||||
cameraConfig &&
|
||||
event &&
|
||||
media &&
|
||||
cameraConfig.frigate.camera_name
|
||||
) {
|
||||
children.push(
|
||||
createChild(
|
||||
getEventTitle(event),
|
||||
getEventMediaContentID(
|
||||
cameraConfig.frigate.client_id,
|
||||
cameraConfig.frigate.camera_name,
|
||||
event.id,
|
||||
media,
|
||||
if (
|
||||
cameraID &&
|
||||
cameraConfig &&
|
||||
event &&
|
||||
media &&
|
||||
cameraConfig.frigate.camera_name
|
||||
) {
|
||||
children.push(
|
||||
createChild(
|
||||
getEventTitle(event),
|
||||
getEventMediaContentID(
|
||||
cameraConfig.frigate.client_id,
|
||||
cameraConfig.frigate.camera_name,
|
||||
event.id,
|
||||
media,
|
||||
),
|
||||
{
|
||||
thumbnail: getEventThumbnailURL(cameraConfig.frigate.client_id, event),
|
||||
event: event,
|
||||
cameraID: cameraID,
|
||||
},
|
||||
),
|
||||
{
|
||||
thumbnail: getEventThumbnailURL(cameraConfig.frigate.client_id, event),
|
||||
event: event,
|
||||
cameraID: cameraID,
|
||||
},
|
||||
),
|
||||
);
|
||||
if (selected.includes(event.id)) {
|
||||
childIndex = children.length - 1;
|
||||
);
|
||||
if (selected.includes(event.id)) {
|
||||
childIndex = children.length - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
if (!children.length) {
|
||||
return null;
|
||||
}
|
||||
@@ -1053,13 +1057,7 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
* Update the timeline from the view object.
|
||||
*/
|
||||
protected async _updateTimelineFromView(): Promise<void> {
|
||||
if (
|
||||
!this.hass ||
|
||||
!this.cameras ||
|
||||
!this.view ||
|
||||
!this.timelineConfig ||
|
||||
!this._timeline
|
||||
) {
|
||||
if (!this.hass || !this.cameras || !this.view || !this.timelineConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1091,7 +1089,7 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
));
|
||||
}
|
||||
|
||||
this._timeline.setSelection(event ? [event.id] : [], {
|
||||
this._timeline?.setSelection(event ? [event.id] : [], {
|
||||
focus: false,
|
||||
animation: {
|
||||
animation: false,
|
||||
@@ -1107,7 +1105,11 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
this.timelineDataManager?.rewriteItem(event.id);
|
||||
}
|
||||
|
||||
if (!this._pointerHeld && !this.view.context?.timeline?.noSetWindow) {
|
||||
if (
|
||||
!this._pointerHeld &&
|
||||
!this.view.context?.timeline?.noSetWindow &&
|
||||
this._timeline
|
||||
) {
|
||||
// Regenerate the thumbnails after the selection, to allow the new selection
|
||||
// to be in the generated view.
|
||||
const context = this.view.context?.timeline;
|
||||
|
||||
+25
-17
@@ -294,7 +294,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
value: 'icons',
|
||||
label: localize('config.common.controls.next_previous.styles.icons'),
|
||||
},
|
||||
{ value: 'none', label: localize('config.common.controls.next_previous.styles.none') },
|
||||
{
|
||||
value: 'none',
|
||||
label: localize('config.common.controls.next_previous.styles.none'),
|
||||
},
|
||||
];
|
||||
|
||||
protected _aspectRatioModes: EditorSelectOption[] = [
|
||||
@@ -336,7 +339,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
|
||||
protected _thumbnailMedias: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'clips', label: localize('config.common.controls.thumbnails.medias.clips') },
|
||||
{
|
||||
value: 'clips',
|
||||
label: localize('config.common.controls.thumbnails.medias.clips'),
|
||||
},
|
||||
{
|
||||
value: 'snapshots',
|
||||
label: localize('config.common.controls.thumbnails.medias.snapshots'),
|
||||
@@ -1388,7 +1394,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
${this._expandedMenus[MENU_OPTIONS] === 'cameras'
|
||||
? html`
|
||||
<div class="values">
|
||||
${cameras.map((_, index) => this._renderCamera(cameras, index, entities))}
|
||||
${cameras.map((_, index) =>
|
||||
this._renderCamera(cameras, index, entities),
|
||||
)}
|
||||
${this._renderCamera(cameras, cameras.length, entities, true)}
|
||||
</div>
|
||||
`
|
||||
@@ -1424,18 +1432,18 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
${this._renderNumberInput(CONF_MENU_BUTTON_SIZE, {
|
||||
min: BUTTON_SIZE_MIN,
|
||||
})}
|
||||
${this._renderMenuButton('frigate') /* */}
|
||||
${this._renderMenuButton('cameras') /* */}
|
||||
${this._renderMenuButton('live') /* */}
|
||||
${this._renderMenuButton('clips') /* */}
|
||||
${this._renderMenuButton('snapshots')}
|
||||
${this._renderMenuButton('image') /* */}
|
||||
${this._renderMenuButton('download')}
|
||||
${this._renderMenuButton('frigate_ui')}
|
||||
${this._renderMenuButton('fullscreen')}
|
||||
${this._renderMenuButton('timeline')}
|
||||
${this._renderMenuButton('media_player')}
|
||||
</div>
|
||||
${this._renderMenuButton('frigate') /* */}
|
||||
${this._renderMenuButton('cameras') /* */}
|
||||
${this._renderMenuButton('live') /* */}
|
||||
${this._renderMenuButton('clips') /* */}
|
||||
${this._renderMenuButton('snapshots')}
|
||||
${this._renderMenuButton('image') /* */}
|
||||
${this._renderMenuButton('download')}
|
||||
${this._renderMenuButton('frigate_ui')}
|
||||
${this._renderMenuButton('fullscreen')}
|
||||
${this._renderMenuButton('timeline')}
|
||||
${this._renderMenuButton('media_player')}
|
||||
`
|
||||
: ''}
|
||||
${this._renderOptionSetHeader('live')}
|
||||
@@ -1493,7 +1501,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
{
|
||||
configPathMedia: CONF_LIVE_CONTROLS_THUMBNAILS_MEDIA,
|
||||
configPathMode: CONF_LIVE_CONTROLS_THUMBNAILS_MODE,
|
||||
}
|
||||
},
|
||||
)}
|
||||
${this._renderTitleControls(
|
||||
MENU_LIVE_CONTROLS_TITLE,
|
||||
@@ -1582,7 +1590,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
||||
{
|
||||
configPathMode: CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_MODE,
|
||||
}
|
||||
},
|
||||
)}
|
||||
${this._renderTitleControls(
|
||||
MENU_MEDIA_VIEWER_CONTROLS_TITLE,
|
||||
@@ -1641,7 +1649,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
||||
{
|
||||
configPathMode: CONF_TIMELINE_CONTROLS_THUMBNAILS_MODE,
|
||||
}
|
||||
},
|
||||
)}
|
||||
</div>`
|
||||
: ''}
|
||||
|
||||
@@ -9,13 +9,19 @@
|
||||
/* Don't drop shadow or have radius for nested webrtc card */
|
||||
#webrtc ha-card {
|
||||
border-radius: 0px;
|
||||
background-color: unset;
|
||||
|
||||
// To get the WebRTC player to line up correctly with some themes.
|
||||
margin: 0px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
ha-card,
|
||||
div.fix-safari,
|
||||
#video {
|
||||
background: unset;
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
#webrtc #video {
|
||||
@include media-layout.media-layout();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user