Show thumbnails as tooltips.
This commit is contained in:
@@ -179,6 +179,7 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
|
||||
.target=${parent}
|
||||
.childIndex=${childIndex}
|
||||
?details=${this._config?.show_details}
|
||||
?controls=${true}
|
||||
thumbnail_size=${ifDefined(this._config?.size)}
|
||||
class="${classMap(classes)}"
|
||||
@click=${(ev) => {
|
||||
|
||||
+68
-32
@@ -2,7 +2,7 @@ import { CSSResult, TemplateResult, html, unsafeCSS, LitElement } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { format, fromUnixTime } from 'date-fns';
|
||||
|
||||
import type { FrigateBrowseMediaSource } from '../types.js';
|
||||
import type { FrigateBrowseMediaSource, FrigateEvent } from '../types.js';
|
||||
import { View } from '../view.js';
|
||||
import {
|
||||
getEventDurationString,
|
||||
@@ -15,6 +15,20 @@ import thumbnailStyle from '../scss/thumbnail.scss';
|
||||
|
||||
@customElement('frigate-card-thumbnail')
|
||||
export class FrigateCardThumbnail extends LitElement {
|
||||
@property({ attribute: true, type: Boolean })
|
||||
public details = false;
|
||||
|
||||
@property({ attribute: true, type: Boolean })
|
||||
public controls = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
set thumbnail_size(size: number) {
|
||||
this.style.setProperty('--frigate-card-thumbnail-size', String(size));
|
||||
}
|
||||
|
||||
// ============================
|
||||
// Data-binding based interface
|
||||
// ============================
|
||||
@property({ attribute: false })
|
||||
protected view?: Readonly<View>;
|
||||
|
||||
@@ -24,34 +38,54 @@ export class FrigateCardThumbnail extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public childIndex?: number;
|
||||
|
||||
@property({ attribute: true, type: Boolean, reflect: true })
|
||||
public details = false;
|
||||
// =============================================================
|
||||
// Overrides that can be used if data bindings are not available
|
||||
// =============================================================
|
||||
@property({ attribute: true })
|
||||
public thumbnail?: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
set thumbnail_size(size: number) {
|
||||
this.style.setProperty('--frigate-card-thumbnail-size', String(size));
|
||||
}
|
||||
@property({ attribute: true })
|
||||
public label?: string;
|
||||
|
||||
@property({ attribute: true })
|
||||
public event?: string;
|
||||
|
||||
/**
|
||||
* Render the element.
|
||||
* @returns A template to display to the user.
|
||||
*/
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this.target || !this.target.children || this.childIndex === undefined) {
|
||||
return;
|
||||
let event: FrigateEvent | null = null;
|
||||
let thumbnail: string | null = null;
|
||||
let label: string | null = null;
|
||||
|
||||
// Take the event / thumbnail / label from the data-bound media (if specified).
|
||||
if (this.target && this.target.children && this.childIndex !== undefined) {
|
||||
const media = this.target.children[this.childIndex];
|
||||
event = media.frigate?.event ?? null;
|
||||
thumbnail = media.thumbnail;
|
||||
label = media.title;
|
||||
}
|
||||
const media = this.target.children[this.childIndex];
|
||||
if (!media.thumbnail) {
|
||||
|
||||
// Always give the overrides preference (if specified).
|
||||
if (this.event) {
|
||||
event = JSON.parse(this.event);
|
||||
}
|
||||
thumbnail = this.thumbnail ? this.thumbnail : thumbnail;
|
||||
label = this.label ? this.label : label;
|
||||
|
||||
if (!thumbnail) {
|
||||
return;
|
||||
}
|
||||
|
||||
const event = media.frigate?.event;
|
||||
console.info;
|
||||
|
||||
return html` <img
|
||||
aria-label="${media.title}"
|
||||
src="${media.thumbnail}"
|
||||
title="${media.title}"
|
||||
aria-label="${label ?? ''}"
|
||||
src="${thumbnail}"
|
||||
title="${label ?? ''}"
|
||||
/>
|
||||
${event?.retain_indefinitely
|
||||
${this.controls && event?.retain_indefinitely
|
||||
? html` <ha-icon
|
||||
class="favorite"
|
||||
icon="mdi:star"
|
||||
@@ -80,22 +114,24 @@ export class FrigateCardThumbnail extends LitElement {
|
||||
</div>
|
||||
</div>`
|
||||
: html``}
|
||||
<ha-icon
|
||||
class="timeline"
|
||||
icon="mdi:target"
|
||||
title=${localize('thumbnail.timeline')}
|
||||
@click=${(ev: Event) => {
|
||||
stopEventFromActivatingCardWideActions(ev);
|
||||
this.view
|
||||
?.evolve({
|
||||
view: 'timeline',
|
||||
target: this.target,
|
||||
childIndex: this.childIndex ?? null,
|
||||
context: {},
|
||||
})
|
||||
.dispatchChangeEvent(this);
|
||||
}}
|
||||
></ha-icon>`;
|
||||
${this.controls
|
||||
? html`<ha-icon
|
||||
class="timeline"
|
||||
icon="mdi:target"
|
||||
title=${localize('thumbnail.timeline')}
|
||||
@click=${(ev: Event) => {
|
||||
stopEventFromActivatingCardWideActions(ev);
|
||||
this.view
|
||||
?.evolve({
|
||||
view: 'timeline',
|
||||
target: this.target,
|
||||
childIndex: this.childIndex ?? null,
|
||||
context: {},
|
||||
})
|
||||
.dispatchChangeEvent(this);
|
||||
}}
|
||||
></ha-icon>`
|
||||
: ''}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// TODO: Hover over an event should show something useful.
|
||||
// TODO: Periodically refetch events.
|
||||
// TODO: Editor support for timeline (incl. in views).
|
||||
// TODO: Make thumbnail controls optional (in all places that use thumbnails).
|
||||
// TODO: Search for TODOs and logging statements.
|
||||
|
||||
import {
|
||||
@@ -74,11 +76,14 @@ class TimelineEventManager {
|
||||
// The latest date managed.
|
||||
protected _dateEnd?: Date;
|
||||
protected _contentCallback?: (source: FrigateBrowseMediaSource) => string;
|
||||
protected _tooltipCallback?: (source: FrigateBrowseMediaSource) => string;
|
||||
|
||||
constructor(params?: {
|
||||
contentCallback?: (source: FrigateBrowseMediaSource) => string;
|
||||
tooltipCallback?: (source: FrigateBrowseMediaSource) => string;
|
||||
}) {
|
||||
this._contentCallback = params?.contentCallback;
|
||||
this._tooltipCallback = params?.tooltipCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,6 +124,7 @@ class TimelineEventManager {
|
||||
id: event.id,
|
||||
group: camera,
|
||||
content: this._contentCallback?.(child) ?? '',
|
||||
title: this._tooltipCallback?.(child) ?? '',
|
||||
start: event.start_time * 1000,
|
||||
event: event,
|
||||
};
|
||||
@@ -306,11 +312,41 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
@property({ attribute: false })
|
||||
protected timelineConfig?: TimelineConfig;
|
||||
|
||||
protected _events = new TimelineEventManager();
|
||||
protected _events = new TimelineEventManager({
|
||||
tooltipCallback: this._getTooltip.bind(this),
|
||||
});
|
||||
protected _refTimeline: Ref<HTMLElement> = createRef();
|
||||
protected _thumbnails?: FrigateBrowseMediaSource;
|
||||
protected _timeline?: Timeline;
|
||||
|
||||
/**
|
||||
* Get a tooltip for a given timeline event.
|
||||
* @param source The FrigateBrowseMediaSource in question.
|
||||
* @returns The tooltip as a string to render.
|
||||
*/
|
||||
protected _getTooltip(source: FrigateBrowseMediaSource): string {
|
||||
const thumbnailSizeAttr = this.timelineConfig
|
||||
? `thumbnail_size="${this.timelineConfig.controls.thumbnails.size}"`
|
||||
: '';
|
||||
const eventAttr = source.frigate?.event
|
||||
? `event='${JSON.stringify(source.frigate.event)}'`
|
||||
: '';
|
||||
console.info(eventAttr);
|
||||
|
||||
// Cannot use Lit data-bindings as visjs requires a string for tooltips.
|
||||
// Note that changes to attributes here must be mirrored in the xss
|
||||
// whitelist in `_getOptions()` .
|
||||
return `
|
||||
<frigate-card-thumbnail
|
||||
details
|
||||
thumbnail="${source.thumbnail}"
|
||||
label="${source.title}"
|
||||
${eventAttr}
|
||||
${thumbnailSizeAttr}
|
||||
>
|
||||
</frigate-card-thumbnail>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Master render method.
|
||||
* @returns A rendered template.
|
||||
@@ -575,14 +611,19 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
start: start,
|
||||
end: end,
|
||||
groupHeightMode: 'fixed',
|
||||
tooltip: {
|
||||
followMouse: true,
|
||||
overflowMethod: 'cap',
|
||||
},
|
||||
xss: {
|
||||
disabled: false,
|
||||
filterOptions: {
|
||||
whiteList: {
|
||||
'frigate-card-timeline-event': [
|
||||
'frigate-card-thumbnail': [
|
||||
'details',
|
||||
'thumbnail',
|
||||
'label',
|
||||
'media_id',
|
||||
'event',
|
||||
'thumbnail_size',
|
||||
],
|
||||
div: ['title'],
|
||||
|
||||
Reference in New Issue
Block a user