Rename surround-thumbnails to just surround.
This commit is contained in:
@@ -61,7 +61,7 @@ import {
|
|||||||
import { dispatchErrorMessageEvent } from './message.js';
|
import { dispatchErrorMessageEvent } from './message.js';
|
||||||
import './next-prev-control.js';
|
import './next-prev-control.js';
|
||||||
import './title-control.js';
|
import './title-control.js';
|
||||||
import './surround-thumbnails';
|
import './surround.js';
|
||||||
import '../patches/ha-camera-stream';
|
import '../patches/ha-camera-stream';
|
||||||
import { EmblaCarouselPlugins } from './carousel.js';
|
import { EmblaCarouselPlugins } from './carousel.js';
|
||||||
import { renderTask } from '../utils/task.js';
|
import { renderTask } from '../utils/task.js';
|
||||||
@@ -213,7 +213,7 @@ export class FrigateCardLive extends LitElement {
|
|||||||
// is received when the card is in the background).
|
// is received when the card is in the background).
|
||||||
const result = html`${keyed(
|
const result = html`${keyed(
|
||||||
this._renderKey,
|
this._renderKey,
|
||||||
html`<frigate-card-surround-thumbnails
|
html`<frigate-card-surround
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.view=${this.view}
|
.view=${this.view}
|
||||||
.thumbnailConfig=${config.controls.thumbnails}
|
.thumbnailConfig=${config.controls.thumbnails}
|
||||||
@@ -251,7 +251,7 @@ export class FrigateCardLive extends LitElement {
|
|||||||
.liveOverrides=${this.liveOverrides}
|
.liveOverrides=${this.liveOverrides}
|
||||||
>
|
>
|
||||||
</frigate-card-live-carousel>
|
</frigate-card-live-carousel>
|
||||||
</frigate-card-surround-thumbnails>`,
|
</frigate-card-surround>`,
|
||||||
)}`;
|
)}`;
|
||||||
|
|
||||||
this._messageReceivedPostRender = false;
|
this._messageReceivedPostRender = false;
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
||||||
|
import { createRef, ref, Ref } from 'lit/directives/ref.js';
|
||||||
|
import { customElement } from 'lit/decorators.js';
|
||||||
|
|
||||||
|
import { FrigateCardDrawer } from './drawer.js';
|
||||||
|
|
||||||
|
import './drawer.js';
|
||||||
|
|
||||||
|
import surroundBasicStyle from '../scss/surround-basic.scss';
|
||||||
|
|
||||||
|
interface FrigateCardDrawerOpen {
|
||||||
|
drawer: 'left' | 'right';
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement('frigate-card-surround-basic')
|
||||||
|
export class FrigateCardSurroundBasic extends LitElement {
|
||||||
|
protected _refDrawerLeft: Ref<FrigateCardDrawer> = createRef();
|
||||||
|
protected _refDrawerRight: Ref<FrigateCardDrawer> = createRef();
|
||||||
|
protected _boundDrawerHandler = this._drawerHandler.bind(this);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component connected callback.
|
||||||
|
*/
|
||||||
|
connectedCallback(): void {
|
||||||
|
super.connectedCallback();
|
||||||
|
this.addEventListener('frigate-card:drawer:open', this._boundDrawerHandler);
|
||||||
|
this.addEventListener('frigate-card:drawer:close', this._boundDrawerHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component disconnected callback.
|
||||||
|
*/
|
||||||
|
disconnectedCallback(): void {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
this.removeEventListener('frigate-card:drawer:open', this._boundDrawerHandler);
|
||||||
|
this.removeEventListener('frigate-card:drawer:close', this._boundDrawerHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _drawerHandler(ev: Event) {
|
||||||
|
const drawer = (ev as CustomEvent<FrigateCardDrawerOpen>).detail.drawer;
|
||||||
|
const open = ev.type.endsWith(':open');
|
||||||
|
if (drawer === 'left' && this._refDrawerLeft.value) {
|
||||||
|
this._refDrawerLeft.value.open = open;
|
||||||
|
} else if (drawer === 'right' && this._refDrawerRight.value) {
|
||||||
|
this._refDrawerRight.value.open = open;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Master render method.
|
||||||
|
* @returns A rendered template.
|
||||||
|
*/
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
return html` <slot name="above"></slot>
|
||||||
|
<slot></slot>
|
||||||
|
<frigate-card-drawer ${ref(this._refDrawerLeft)} location="left">
|
||||||
|
<slot name="left"></slot>
|
||||||
|
</frigate-card-drawer>
|
||||||
|
<frigate-card-drawer ${ref(this._refDrawerRight)} location="right">
|
||||||
|
<slot name="right"></slot>
|
||||||
|
</frigate-card-drawer>
|
||||||
|
<slot name="below"></slot>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return compiled CSS styles.
|
||||||
|
*/
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return unsafeCSS(surroundBasicStyle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'frigate-card-surround-basic': FrigateCardSurroundBasic;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
import './surround.js';
|
|
||||||
import './timeline-core.js';
|
|
||||||
|
|
||||||
import {
|
|
||||||
CSSResultGroup,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
PropertyValues,
|
|
||||||
TemplateResult,
|
|
||||||
unsafeCSS,
|
|
||||||
} from 'lit';
|
|
||||||
import { customElement, property } from 'lit/decorators.js';
|
|
||||||
|
|
||||||
import surroundThumbnailsStyle from '../scss/surround.scss';
|
|
||||||
import {
|
|
||||||
BrowseMediaQueryParameters,
|
|
||||||
CameraConfig,
|
|
||||||
ExtendedHomeAssistant,
|
|
||||||
FrigateBrowseMediaSource,
|
|
||||||
FrigateCardError,
|
|
||||||
MiniTimelineControlConfig,
|
|
||||||
ThumbnailsControlConfig,
|
|
||||||
} from '../types.js';
|
|
||||||
import { contentsChanged, dispatchFrigateCardEvent } from '../utils/basic.js';
|
|
||||||
import {
|
|
||||||
getFirstTrueMediaChildIndex,
|
|
||||||
multipleBrowseMediaQueryMerged,
|
|
||||||
} from '../utils/ha/browse-media';
|
|
||||||
import { TimelineDataManager } from '../utils/timeline-data-manager';
|
|
||||||
import { View } from '../view.js';
|
|
||||||
import { dispatchFrigateCardErrorEvent } from './message.js';
|
|
||||||
import { ThumbnailCarouselTap } from './thumbnail-carousel.js';
|
|
||||||
|
|
||||||
interface ThumbnailViewContext {
|
|
||||||
// Whetherr or not to fetch thumbnails.
|
|
||||||
fetch?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module 'view' {
|
|
||||||
interface ViewContext {
|
|
||||||
thumbnails?: ThumbnailViewContext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@customElement('frigate-card-surround-thumbnails')
|
|
||||||
export class FrigateCardSurroundThumbnails extends LitElement {
|
|
||||||
@property({ attribute: false })
|
|
||||||
public hass?: ExtendedHomeAssistant;
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
public view?: Readonly<View>;
|
|
||||||
|
|
||||||
@property({ attribute: false, hasChanged: contentsChanged })
|
|
||||||
public thumbnailConfig?: ThumbnailsControlConfig;
|
|
||||||
|
|
||||||
@property({ attribute: false, hasChanged: contentsChanged })
|
|
||||||
public timelineConfig?: MiniTimelineControlConfig;
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
public inBackground?: boolean;
|
|
||||||
|
|
||||||
@property({ attribute: false, hasChanged: contentsChanged })
|
|
||||||
public browseMediaParams?: BrowseMediaQueryParameters | BrowseMediaQueryParameters[];
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
public cameras?: Map<string, CameraConfig>;
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
public timelineDataManager?: TimelineDataManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch thumbnail media when a target is not specified in the view (e.g. for
|
|
||||||
* the live view).
|
|
||||||
* @param param Task parameters.
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
protected async _fetchMedia(): Promise<void> {
|
|
||||||
if (
|
|
||||||
this.inBackground ||
|
|
||||||
!this.hass ||
|
|
||||||
!this.view ||
|
|
||||||
!this.thumbnailConfig ||
|
|
||||||
this.thumbnailConfig.mode === 'none' ||
|
|
||||||
this.view.target ||
|
|
||||||
!this.browseMediaParams ||
|
|
||||||
!(this.view.context?.thumbnails?.fetch ?? true)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let parent: FrigateBrowseMediaSource | null;
|
|
||||||
try {
|
|
||||||
parent = await multipleBrowseMediaQueryMerged(this.hass, this.browseMediaParams);
|
|
||||||
} catch (e) {
|
|
||||||
return dispatchFrigateCardErrorEvent(this, e as FrigateCardError);
|
|
||||||
}
|
|
||||||
if (getFirstTrueMediaChildIndex(parent) !== null) {
|
|
||||||
this.view
|
|
||||||
?.evolve({
|
|
||||||
target: parent,
|
|
||||||
childIndex: null,
|
|
||||||
|
|
||||||
// Don't carry over history of this 'empty' view.
|
|
||||||
previous: null,
|
|
||||||
})
|
|
||||||
.dispatchChangeEvent(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if a drawer is being used.
|
|
||||||
* @returns `true` if a drawer is used, `false` otherwise.
|
|
||||||
*/
|
|
||||||
protected _hasDrawer(): boolean {
|
|
||||||
return (
|
|
||||||
!!this.thumbnailConfig && ['left', 'right'].includes(this.thumbnailConfig.mode)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called before each update.
|
|
||||||
*/
|
|
||||||
protected willUpdate(changedProperties: PropertyValues): void {
|
|
||||||
// Once the component will certainly update, dispatch a media request. Only
|
|
||||||
// do so if properties relevant to the request have changed (as per their
|
|
||||||
// hasChanged).
|
|
||||||
if (
|
|
||||||
['view', 'fetch', 'browseMediaParams', 'inBackground'].some((prop) =>
|
|
||||||
changedProperties.has(prop),
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
this._fetchMedia();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Master render method.
|
|
||||||
* @returns A rendered template.
|
|
||||||
*/
|
|
||||||
protected render(): TemplateResult | void {
|
|
||||||
if (!this.hass || !this.view || !this.thumbnailConfig) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const changeDrawer = (ev: CustomEvent, action: 'open' | 'close') => {
|
|
||||||
// The event catch/re-dispatch below protect encapsulation: Catches the
|
|
||||||
// request to view thumbnails and re-dispatches a request to open the drawer
|
|
||||||
// (if the thumbnails are in a drawer). The new event needs to be dispatched
|
|
||||||
// from the origin of the inbound event, so it can be handled by
|
|
||||||
// <frigate-card-surround> .
|
|
||||||
if (this.thumbnailConfig && this._hasDrawer()) {
|
|
||||||
dispatchFrigateCardEvent(ev.composedPath()[0], 'drawer:' + action, {
|
|
||||||
drawer: this.thumbnailConfig.mode,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return html` <frigate-card-surround
|
|
||||||
@frigate-card:thumbnails:open=${(ev: CustomEvent) => changeDrawer(ev, 'open')}
|
|
||||||
@frigate-card:thumbnails:close=${(ev: CustomEvent) => changeDrawer(ev, 'close')}
|
|
||||||
>
|
|
||||||
${this.thumbnailConfig &&
|
|
||||||
this.thumbnailConfig.mode !== 'none' &&
|
|
||||||
!this.inBackground
|
|
||||||
? html` <frigate-card-thumbnail-carousel
|
|
||||||
slot=${this.thumbnailConfig.mode}
|
|
||||||
.hass=${this.hass}
|
|
||||||
.config=${this.thumbnailConfig}
|
|
||||||
.view=${this.view}
|
|
||||||
.target=${this.view.target}
|
|
||||||
.selected=${this.view.childIndex}
|
|
||||||
.cameras=${this.cameras}
|
|
||||||
@frigate-card:view:change=${(ev: CustomEvent) => changeDrawer(ev, 'close')}
|
|
||||||
@frigate-card:thumbnail-carousel:tap=${(
|
|
||||||
ev: CustomEvent<ThumbnailCarouselTap>,
|
|
||||||
) => {
|
|
||||||
const child: FrigateBrowseMediaSource | null =
|
|
||||||
ev.detail.target?.children?.[ev.detail.childIndex] ?? null;
|
|
||||||
// Send the view change from the source of the tap event, so the
|
|
||||||
// view change will be caught by the handler above (to close the drawer).
|
|
||||||
if (child) {
|
|
||||||
this.view
|
|
||||||
?.evolve({
|
|
||||||
view: this.view.is('recording') ? 'recording' : 'media',
|
|
||||||
target: ev.detail.target,
|
|
||||||
childIndex: ev.detail.childIndex,
|
|
||||||
context: null,
|
|
||||||
...(child?.frigate?.cameraID && {
|
|
||||||
camera: child?.frigate?.cameraID,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.dispatchChangeEvent(ev.composedPath()[0]);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
</frigate-card-thumbnail-carousel>`
|
|
||||||
: ''}
|
|
||||||
${this.timelineConfig && !this.inBackground
|
|
||||||
? html` <frigate-card-timeline-core
|
|
||||||
slot=${this.timelineConfig.mode}
|
|
||||||
.hass=${this.hass}
|
|
||||||
.view=${this.view}
|
|
||||||
.cameras=${this.cameras}
|
|
||||||
.mini=${true}
|
|
||||||
.timelineConfig=${this.timelineConfig}
|
|
||||||
.thumbnailDetails=${this.thumbnailConfig?.show_details}
|
|
||||||
.thumbnailSize=${this.thumbnailConfig?.size}
|
|
||||||
.timelineDataManager=${this.timelineDataManager}
|
|
||||||
>
|
|
||||||
</frigate-card-timeline-core>`
|
|
||||||
: ''}
|
|
||||||
<slot></slot>
|
|
||||||
</frigate-card-surround>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return compiled CSS styles.
|
|
||||||
*/
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return unsafeCSS(surroundThumbnailsStyle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
'frigate-card-surround-thumbnails': FrigateCardSurroundThumbnails;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+190
-40
@@ -1,48 +1,134 @@
|
|||||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
import {
|
||||||
import { createRef, ref, Ref } from 'lit/directives/ref.js';
|
CSSResultGroup,
|
||||||
import { customElement } from 'lit/decorators.js';
|
html,
|
||||||
|
LitElement,
|
||||||
import { FrigateCardDrawer } from './drawer.js';
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
import './drawer.js';
|
unsafeCSS,
|
||||||
|
} from 'lit';
|
||||||
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
|
|
||||||
import surroundStyle from '../scss/surround.scss';
|
import surroundStyle from '../scss/surround.scss';
|
||||||
|
import {
|
||||||
|
BrowseMediaQueryParameters,
|
||||||
|
CameraConfig,
|
||||||
|
ExtendedHomeAssistant,
|
||||||
|
FrigateBrowseMediaSource,
|
||||||
|
FrigateCardError,
|
||||||
|
MiniTimelineControlConfig,
|
||||||
|
ThumbnailsControlConfig,
|
||||||
|
} from '../types.js';
|
||||||
|
import { contentsChanged, dispatchFrigateCardEvent } from '../utils/basic.js';
|
||||||
|
import {
|
||||||
|
getFirstTrueMediaChildIndex,
|
||||||
|
multipleBrowseMediaQueryMerged,
|
||||||
|
} from '../utils/ha/browse-media';
|
||||||
|
import { TimelineDataManager } from '../utils/timeline-data-manager';
|
||||||
|
import { View } from '../view.js';
|
||||||
|
import { dispatchFrigateCardErrorEvent } from './message.js';
|
||||||
|
import { ThumbnailCarouselTap } from './thumbnail-carousel.js';
|
||||||
|
|
||||||
interface FrigateCardDrawerOpen {
|
import './surround-basic.js';
|
||||||
drawer: 'left' | 'right';
|
import './timeline-core.js';
|
||||||
|
|
||||||
|
interface ThumbnailViewContext {
|
||||||
|
// Whetherr or not to fetch thumbnails.
|
||||||
|
fetch?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'view' {
|
||||||
|
interface ViewContext {
|
||||||
|
thumbnails?: ThumbnailViewContext;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement('frigate-card-surround')
|
@customElement('frigate-card-surround')
|
||||||
export class FrigateCardSurround extends LitElement {
|
export class FrigateCardSurround extends LitElement {
|
||||||
protected _refDrawerLeft: Ref<FrigateCardDrawer> = createRef();
|
@property({ attribute: false })
|
||||||
protected _refDrawerRight: Ref<FrigateCardDrawer> = createRef();
|
public hass?: ExtendedHomeAssistant;
|
||||||
protected _boundDrawerHandler = this._drawerHandler.bind(this);
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
public view?: Readonly<View>;
|
||||||
|
|
||||||
|
@property({ attribute: false, hasChanged: contentsChanged })
|
||||||
|
public thumbnailConfig?: ThumbnailsControlConfig;
|
||||||
|
|
||||||
|
@property({ attribute: false, hasChanged: contentsChanged })
|
||||||
|
public timelineConfig?: MiniTimelineControlConfig;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
public inBackground?: boolean;
|
||||||
|
|
||||||
|
@property({ attribute: false, hasChanged: contentsChanged })
|
||||||
|
public browseMediaParams?: BrowseMediaQueryParameters | BrowseMediaQueryParameters[];
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
public cameras?: Map<string, CameraConfig>;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
public timelineDataManager?: TimelineDataManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component connected callback.
|
* Fetch thumbnail media when a target is not specified in the view (e.g. for
|
||||||
|
* the live view).
|
||||||
|
* @param param Task parameters.
|
||||||
|
* @returns
|
||||||
*/
|
*/
|
||||||
connectedCallback(): void {
|
protected async _fetchMedia(): Promise<void> {
|
||||||
super.connectedCallback();
|
if (
|
||||||
this.addEventListener('frigate-card:drawer:open', this._boundDrawerHandler);
|
this.inBackground ||
|
||||||
this.addEventListener('frigate-card:drawer:close', this._boundDrawerHandler);
|
!this.hass ||
|
||||||
|
!this.view ||
|
||||||
|
!this.thumbnailConfig ||
|
||||||
|
this.thumbnailConfig.mode === 'none' ||
|
||||||
|
this.view.target ||
|
||||||
|
!this.browseMediaParams ||
|
||||||
|
!(this.view.context?.thumbnails?.fetch ?? true)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let parent: FrigateBrowseMediaSource | null;
|
||||||
|
try {
|
||||||
|
parent = await multipleBrowseMediaQueryMerged(this.hass, this.browseMediaParams);
|
||||||
|
} catch (e) {
|
||||||
|
return dispatchFrigateCardErrorEvent(this, e as FrigateCardError);
|
||||||
|
}
|
||||||
|
if (getFirstTrueMediaChildIndex(parent) !== null) {
|
||||||
|
this.view
|
||||||
|
?.evolve({
|
||||||
|
target: parent,
|
||||||
|
childIndex: null,
|
||||||
|
|
||||||
|
// Don't carry over history of this 'empty' view.
|
||||||
|
previous: null,
|
||||||
|
})
|
||||||
|
.dispatchChangeEvent(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component disconnected callback.
|
* Determine if a drawer is being used.
|
||||||
|
* @returns `true` if a drawer is used, `false` otherwise.
|
||||||
*/
|
*/
|
||||||
disconnectedCallback(): void {
|
protected _hasDrawer(): boolean {
|
||||||
super.disconnectedCallback();
|
return (
|
||||||
this.removeEventListener('frigate-card:drawer:open', this._boundDrawerHandler);
|
!!this.thumbnailConfig && ['left', 'right'].includes(this.thumbnailConfig.mode)
|
||||||
this.removeEventListener('frigate-card:drawer:close', this._boundDrawerHandler);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _drawerHandler(ev: Event) {
|
/**
|
||||||
const drawer = (ev as CustomEvent<FrigateCardDrawerOpen>).detail.drawer;
|
* Called before each update.
|
||||||
const open = ev.type.endsWith(':open');
|
*/
|
||||||
if (drawer === 'left' && this._refDrawerLeft.value) {
|
protected willUpdate(changedProperties: PropertyValues): void {
|
||||||
this._refDrawerLeft.value.open = open;
|
// Once the component will certainly update, dispatch a media request. Only
|
||||||
} else if (drawer === 'right' && this._refDrawerRight.value) {
|
// do so if properties relevant to the request have changed (as per their
|
||||||
this._refDrawerRight.value.open = open;
|
// hasChanged).
|
||||||
|
if (
|
||||||
|
['view', 'fetch', 'browseMediaParams', 'inBackground'].some((prop) =>
|
||||||
|
changedProperties.has(prop),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
this._fetchMedia();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,15 +137,79 @@ export class FrigateCardSurround extends LitElement {
|
|||||||
* @returns A rendered template.
|
* @returns A rendered template.
|
||||||
*/
|
*/
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
return html` <slot name="above"></slot>
|
if (!this.hass || !this.view || !this.thumbnailConfig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeDrawer = (ev: CustomEvent, action: 'open' | 'close') => {
|
||||||
|
// The event catch/re-dispatch below protect encapsulation: Catches the
|
||||||
|
// request to view thumbnails and re-dispatches a request to open the drawer
|
||||||
|
// (if the thumbnails are in a drawer). The new event needs to be dispatched
|
||||||
|
// from the origin of the inbound event, so it can be handled by
|
||||||
|
// <frigate-card-surround> .
|
||||||
|
if (this.thumbnailConfig && this._hasDrawer()) {
|
||||||
|
dispatchFrigateCardEvent(ev.composedPath()[0], 'drawer:' + action, {
|
||||||
|
drawer: this.thumbnailConfig.mode,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return html` <frigate-card-surround-basic
|
||||||
|
@frigate-card:thumbnails:open=${(ev: CustomEvent) => changeDrawer(ev, 'open')}
|
||||||
|
@frigate-card:thumbnails:close=${(ev: CustomEvent) => changeDrawer(ev, 'close')}
|
||||||
|
>
|
||||||
|
${this.thumbnailConfig &&
|
||||||
|
this.thumbnailConfig.mode !== 'none' &&
|
||||||
|
!this.inBackground
|
||||||
|
? html` <frigate-card-thumbnail-carousel
|
||||||
|
slot=${this.thumbnailConfig.mode}
|
||||||
|
.hass=${this.hass}
|
||||||
|
.config=${this.thumbnailConfig}
|
||||||
|
.view=${this.view}
|
||||||
|
.target=${this.view.target}
|
||||||
|
.selected=${this.view.childIndex}
|
||||||
|
.cameras=${this.cameras}
|
||||||
|
@frigate-card:view:change=${(ev: CustomEvent) => changeDrawer(ev, 'close')}
|
||||||
|
@frigate-card:thumbnail-carousel:tap=${(
|
||||||
|
ev: CustomEvent<ThumbnailCarouselTap>,
|
||||||
|
) => {
|
||||||
|
const child: FrigateBrowseMediaSource | null =
|
||||||
|
ev.detail.target?.children?.[ev.detail.childIndex] ?? null;
|
||||||
|
// Send the view change from the source of the tap event, so the
|
||||||
|
// view change will be caught by the handler above (to close the drawer).
|
||||||
|
if (child) {
|
||||||
|
this.view
|
||||||
|
?.evolve({
|
||||||
|
view: this.view.is('recording') ? 'recording' : 'media',
|
||||||
|
target: ev.detail.target,
|
||||||
|
childIndex: ev.detail.childIndex,
|
||||||
|
context: null,
|
||||||
|
...(child?.frigate?.cameraID && {
|
||||||
|
camera: child?.frigate?.cameraID,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.dispatchChangeEvent(ev.composedPath()[0]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
</frigate-card-thumbnail-carousel>`
|
||||||
|
: ''}
|
||||||
|
${this.timelineConfig && !this.inBackground
|
||||||
|
? html` <frigate-card-timeline-core
|
||||||
|
slot=${this.timelineConfig.mode}
|
||||||
|
.hass=${this.hass}
|
||||||
|
.view=${this.view}
|
||||||
|
.cameras=${this.cameras}
|
||||||
|
.mini=${true}
|
||||||
|
.timelineConfig=${this.timelineConfig}
|
||||||
|
.thumbnailDetails=${this.thumbnailConfig?.show_details}
|
||||||
|
.thumbnailSize=${this.thumbnailConfig?.size}
|
||||||
|
.timelineDataManager=${this.timelineDataManager}
|
||||||
|
>
|
||||||
|
</frigate-card-timeline-core>`
|
||||||
|
: ''}
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<frigate-card-drawer ${ref(this._refDrawerLeft)} location="left">
|
</frigate-card-surround-basic>`;
|
||||||
<slot name="left"></slot>
|
|
||||||
</frigate-card-drawer>
|
|
||||||
<frigate-card-drawer ${ref(this._refDrawerRight)} location="right">
|
|
||||||
<slot name="right"></slot>
|
|
||||||
</frigate-card-drawer>
|
|
||||||
<slot name="below"></slot>`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +221,7 @@ export class FrigateCardSurround extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"frigate-card-surround": FrigateCardSurround
|
'frigate-card-surround': FrigateCardSurround;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// TODO: When a media viewer is first loaded the selected child won't work (because the underlying carousel has not yet rendered)
|
// TODO: When a media viewer is first loaded the selected child won't work (because the underlying carousel has not yet rendered)
|
||||||
// TODO: rename surround to surround basic and this file to surround?
|
|
||||||
// TODO: thumbnails in drawers don't work.
|
// TODO: thumbnails in drawers don't work.
|
||||||
// TODO: delete segments if not in summary? is this actually necessary? could it create gaps in data? better off stopping access via summary?
|
// TODO: delete segments if not in summary? is this actually necessary? could it create gaps in data? better off stopping access via summary?
|
||||||
// TODO: support filtering created dataviews by recordings or mediatype (so storage )
|
// TODO: support filtering created dataviews by recordings or mediatype (so storage )
|
||||||
// TODO: dataview refresh instead of rewriteitem?
|
// TODO: dataview refresh instead of rewriteitem?
|
||||||
// TODO: Make minitimeline configurable in the editor
|
// TODO: Make minitimeline configurable in the editor
|
||||||
|
// TODO: Is it really useful to select the children in the main timeline view on range change?
|
||||||
|
|
||||||
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
||||||
import { customElement, property } from 'lit/decorators.js';
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
@@ -12,12 +12,12 @@ import timelineStyle from '../scss/timeline.scss';
|
|||||||
import { CameraConfig, ExtendedHomeAssistant, TimelineConfig } from '../types';
|
import { CameraConfig, ExtendedHomeAssistant, TimelineConfig } from '../types';
|
||||||
import { TimelineDataManager } from '../utils/timeline-data-manager';
|
import { TimelineDataManager } from '../utils/timeline-data-manager';
|
||||||
import { View } from '../view';
|
import { View } from '../view';
|
||||||
import './surround-thumbnails.js';
|
import './surround.js';
|
||||||
import './timeline-core.js';
|
import './timeline-core.js';
|
||||||
|
|
||||||
// This file is kept separate from timeline-core.ts to avoid a circular dependency:
|
// This file is kept separate from timeline-core.ts to avoid a circular dependency:
|
||||||
// FrigateCardTimeline ->
|
// FrigateCardTimeline ->
|
||||||
// FrigateCardSurroundThumbnails ->
|
// FrigateCardSurround ->
|
||||||
// FrigateCardTimelineCore
|
// FrigateCardTimelineCore
|
||||||
|
|
||||||
@customElement('frigate-card-timeline')
|
@customElement('frigate-card-timeline')
|
||||||
@@ -46,7 +46,7 @@ export class FrigateCardTimeline extends LitElement {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html` <frigate-card-surround-thumbnails
|
return html` <frigate-card-surround
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.view=${this.view}
|
.view=${this.view}
|
||||||
.thumbnailConfig=${this.timelineConfig.controls.thumbnails}
|
.thumbnailConfig=${this.timelineConfig.controls.thumbnails}
|
||||||
@@ -62,7 +62,7 @@ export class FrigateCardTimeline extends LitElement {
|
|||||||
.timelineDataManager=${this.timelineDataManager}
|
.timelineDataManager=${this.timelineDataManager}
|
||||||
>
|
>
|
||||||
</frigate-card-timeline-core>
|
</frigate-card-timeline-core>
|
||||||
</frigate-card-surround-thumbnails>`;
|
</frigate-card-surround>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ import {
|
|||||||
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-thumbnails';
|
import './surround.js';
|
||||||
import { EmblaCarouselPlugins } from './carousel.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';
|
||||||
@@ -134,7 +134,7 @@ export class FrigateCardViewer extends LitElement {
|
|||||||
return renderProgressIndicator();
|
return renderProgressIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
return html` <frigate-card-surround-thumbnails
|
return html` <frigate-card-surround
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.view=${this.view}
|
.view=${this.view}
|
||||||
.thumbnailConfig=${this.viewerConfig.controls.thumbnails}
|
.thumbnailConfig=${this.viewerConfig.controls.thumbnails}
|
||||||
@@ -150,7 +150,7 @@ export class FrigateCardViewer extends LitElement {
|
|||||||
.resolvedMediaCache=${this.resolvedMediaCache}
|
.resolvedMediaCache=${this.resolvedMediaCache}
|
||||||
>
|
>
|
||||||
</frigate-card-viewer-carousel>
|
</frigate-card-viewer-carousel>
|
||||||
</frigate-card-surround-thumbnails>`;
|
</frigate-card-surround>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
:host {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
// Share the screen space with thumbnails that may be above/below.
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
// Set the drawer relative to this host.
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
// Hide any content outside the main pane (e.g. side drawers) to ensure the
|
||||||
|
// user cannot scroll across to the drawers without opening them.
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
::slotted:not([name]) {
|
||||||
|
// Expand the main body to fill available content not otherwise used by the
|
||||||
|
// surround.
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
:host {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
+1
-17
@@ -1,21 +1,5 @@
|
|||||||
:host {
|
:host {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
display: block;
|
||||||
// Share the screen space with thumbnails that may be above/below.
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
// Set the drawer relative to this host.
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
// Hide any content outside the main pane (e.g. side drawers) to ensure the
|
|
||||||
// user cannot scroll across to the drawers without opening them.
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
::slotted:not([name]) {
|
|
||||||
// Expand the main body to fill available content not otherwise used by the
|
|
||||||
// surround.
|
|
||||||
flex: 1;
|
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ div.timeline {
|
|||||||
// ensure the background (recordings) always span the full height. Otherwise, in
|
// ensure the background (recordings) always span the full height. Otherwise, in
|
||||||
// cases where there are no events, the background is incorrectly rendered too
|
// cases where there are no events, the background is incorrectly rendered too
|
||||||
// short by visjs.
|
// short by visjs.
|
||||||
:host:not([groups]) .vis-item.vis-background {
|
:host(:not([groups])) .vis-item.vis-background {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user