Fullscreen improvements (esp. portrait media)
This commit is contained in:
+36
-12
@@ -192,7 +192,7 @@ export class FrigateCard extends LitElement {
|
|||||||
description: localize('menu.frigate_ui'),
|
description: localize('menu.frigate_ui'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ((this.config.menu_buttons?.fullscreen ?? false) && screenfull.isEnabled) {
|
if ((this.config.menu_buttons?.fullscreen ?? true) && screenfull.isEnabled) {
|
||||||
buttons.set('fullscreen', {
|
buttons.set('fullscreen', {
|
||||||
icon: screenfull.isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen',
|
icon: screenfull.isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen',
|
||||||
description: localize('menu.fullscreen'),
|
description: localize('menu.fullscreen'),
|
||||||
@@ -418,14 +418,15 @@ export class FrigateCard extends LitElement {
|
|||||||
protected _getAspectRatioPadding(): number | null {
|
protected _getAspectRatioPadding(): number | null {
|
||||||
const aspect_ratio_mode = this.config.dimensions?.aspect_ratio_mode ?? 'dynamic';
|
const aspect_ratio_mode = this.config.dimensions?.aspect_ratio_mode ?? 'dynamic';
|
||||||
|
|
||||||
// Do not constrain aspect ratio if either it's entire disabled or it's a
|
// Do not artifically constrain aspect ratio if:
|
||||||
// media view (i.e. not the gallery) and there's a loaded media item in
|
// - It's fullscreen.
|
||||||
// dynamic mode (as the aspect_ratio is essentially whatever the media
|
// - Aspect ratio enforcement is disabled.
|
||||||
// dimensions are).
|
// - Or it's a media view (i.e. not the gallery) and there's a loaded media
|
||||||
|
// item.
|
||||||
if (
|
if (
|
||||||
(screenfull.isEnabled && screenfull.isFullscreen) ||
|
(screenfull.isEnabled && screenfull.isFullscreen) ||
|
||||||
aspect_ratio_mode == 'unconstrained' ||
|
aspect_ratio_mode == 'unconstrained' ||
|
||||||
(!this._view.isGalleryView() && aspect_ratio_mode == 'dynamic' && this._mediaInfo)
|
(this._view.isMediaView() && aspect_ratio_mode == 'dynamic' && this._mediaInfo)
|
||||||
) {
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -452,11 +453,34 @@ export class FrigateCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const padding = this._getAspectRatioPadding();
|
const padding = this._getAspectRatioPadding();
|
||||||
let containerStyleMap = {};
|
const outerStyle = {}, innerStyle = {};
|
||||||
|
|
||||||
|
// Padding to force a particular aspect ratio.
|
||||||
if (padding != null) {
|
if (padding != null) {
|
||||||
containerStyleMap = {
|
outerStyle['padding-top'] = `${padding}%`;
|
||||||
'padding-top': `${padding}%`,
|
}
|
||||||
};
|
|
||||||
|
// Special treatment required when:
|
||||||
|
//
|
||||||
|
// - It's in fullscreen mode
|
||||||
|
// - It's viewing a media clip
|
||||||
|
// - And the media clip is taller than wider (portrait)
|
||||||
|
//
|
||||||
|
// We cannot seem to scale the video by height in CSS without actually
|
||||||
|
// styling the underlying video element (which we do not have access to as
|
||||||
|
// it's buried past multiple shadow roots), so instead scale the width in
|
||||||
|
// terms of'vh' (viewport height) in proportion to the aspect-ratio of the
|
||||||
|
// media.
|
||||||
|
if (
|
||||||
|
screenfull.isEnabled &&
|
||||||
|
screenfull.isFullscreen &&
|
||||||
|
this._view.isMediaView() &&
|
||||||
|
this._mediaInfo &&
|
||||||
|
this._mediaInfo.width < this._mediaInfo.height
|
||||||
|
) {
|
||||||
|
innerStyle['max-width'] = `${
|
||||||
|
(100 * this._mediaInfo.width) / this._mediaInfo.height
|
||||||
|
}vh`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentClasses = {
|
const contentClasses = {
|
||||||
@@ -466,8 +490,8 @@ export class FrigateCard extends LitElement {
|
|||||||
|
|
||||||
return html` <ha-card @click=${this._interactionHandler}>
|
return html` <ha-card @click=${this._interactionHandler}>
|
||||||
${this.config.menu_mode == 'above' ? this._renderMenu() : ''}
|
${this.config.menu_mode == 'above' ? this._renderMenu() : ''}
|
||||||
<div class="container outer" style="${styleMap(containerStyleMap)}">
|
<div class="container outer" style="${styleMap(outerStyle)}">
|
||||||
<div class="${classMap(contentClasses)}">
|
<div class="${classMap(contentClasses)}" style="${styleMap(innerStyle)}">
|
||||||
${this._view.is('clips') || this._view.is('snapshots')
|
${this._view.is('clips') || this._view.is('snapshots')
|
||||||
? html` <frigate-card-gallery
|
? html` <frigate-card-gallery
|
||||||
.hass=${this._hass}
|
.hass=${this._hass}
|
||||||
|
|||||||
@@ -386,6 +386,18 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
|
<br />
|
||||||
|
<ha-formfield
|
||||||
|
.label=${localize('editor.show_button') +
|
||||||
|
': ' +
|
||||||
|
localize('menu.fullscreen')}
|
||||||
|
>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${this._config?.menu_buttons?.frigate_ui ?? true}
|
||||||
|
.configValue=${'menu_buttons.fullscreen'}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
<br />`
|
<br />`
|
||||||
: ''}
|
: ''}
|
||||||
<div class="option" @click=${this._toggleOption} .option=${'advanced'}>
|
<div class="option" @click=${this._toggleOption} .option=${'advanced'}>
|
||||||
|
|||||||
@@ -31,8 +31,10 @@ customElements.whenDefined("ha-hls-player").then(() => {
|
|||||||
.muted=${this.muted}
|
.muted=${this.muted}
|
||||||
?playsinline=${this.playsInline}
|
?playsinline=${this.playsInline}
|
||||||
?controls=${this.controls}
|
?controls=${this.controls}
|
||||||
@loadedmetadata=${(e) => dispatchMediaLoadEvent(this, e)}
|
@loadeddata=${(e) => {
|
||||||
@loadeddata=${this._elementResized}
|
this._elementResized();
|
||||||
|
dispatchMediaLoadEvent(this, e);
|
||||||
|
}}
|
||||||
@pause=${() => dispatchPauseEvent(this)}
|
@pause=${() => dispatchPauseEvent(this)}
|
||||||
@play=${() => dispatchPlayEvent(this)}
|
@play=${() => dispatchPlayEvent(this)}
|
||||||
></video>
|
></video>
|
||||||
|
|||||||
+6
-6
@@ -2,19 +2,18 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
margin: auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.frigate-card-contents {
|
.frigate-card-contents {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
margin: auto;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
-ms-overflow-style: none; /* Hide scrollbar: IE and Edge */
|
-ms-overflow-style: none; /* Hide scrollbar: IE and Edge */
|
||||||
scrollbar-width: none; /* Hide scrollbar: Firefox */
|
scrollbar-width: none; /* Hide scrollbar: Firefox */
|
||||||
|
|
||||||
// Vertically align items in case the container is larger than the media (e.g.
|
|
||||||
// fullscreen mode, or forced aspect ratios).
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||||
@@ -61,6 +60,7 @@ ha-card a {
|
|||||||
|
|
||||||
frigate-card-gallery, frigate-card-viewer, frigate-card-live {
|
frigate-card-gallery, frigate-card-viewer, frigate-card-live {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
frigate-card-gallery {
|
frigate-card-gallery {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
+1
-1
@@ -82,7 +82,7 @@ export const frigateCardConfigSchema = z.object({
|
|||||||
clips: z.boolean().default(true),
|
clips: z.boolean().default(true),
|
||||||
snapshots: z.boolean().default(true),
|
snapshots: z.boolean().default(true),
|
||||||
frigate_ui: z.boolean().default(true),
|
frigate_ui: z.boolean().default(true),
|
||||||
fullscreen: z.boolean().default(false),
|
fullscreen: z.boolean().default(true),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
entities: z
|
entities: z
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ export class View {
|
|||||||
return this.view == 'clips' || this.view == 'snapshots';
|
return this.view == 'clips' || this.view == 'snapshots';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isMediaView(): boolean {
|
||||||
|
return !this.isGalleryView();
|
||||||
|
}
|
||||||
|
|
||||||
get media(): BrowseMediaSource | undefined {
|
get media(): BrowseMediaSource | undefined {
|
||||||
if (this.target) {
|
if (this.target) {
|
||||||
if (this.target.children && this.childIndex !== undefined) {
|
if (this.target.children && this.childIndex !== undefined) {
|
||||||
|
|||||||
Reference in New Issue
Block a user