diff --git a/src/components/timeline.ts b/src/components/timeline.ts index 43f8be2e..88562e37 100644 --- a/src/components/timeline.ts +++ b/src/components/timeline.ts @@ -13,11 +13,13 @@ import { customElement, property } from 'lit/decorators.js'; import { createRef, ref, Ref } from 'lit/directives/ref'; import { BrowseMediaUtil } from '../browse-media-util'; -import { CameraConfig, ExtendedHomeAssistant } from '../types'; +import { CameraConfig, ExtendedHomeAssistant, FrigateBrowseMediaSource } from '../types'; import { View } from '../view'; +import { dispatchFrigateCardEvent } from '../common.js'; import { renderProgressIndicator } from './message'; import timelineStyle from '../scss/timeline.scss'; +import timelineEventStyle from '../scss/timeline-event.scss'; interface FrigateCardTimelineData { id: string; @@ -25,6 +27,40 @@ interface FrigateCardTimelineData { start: number; } +@customElement('frigate-card-timeline-event') +export class FrigateCardTimelineEvent extends LitElement { + @property({ attribute: true }) + protected media_id?: string; + + @property({ attribute: true }) + protected thumbnail?: string; + + @property({ attribute: true }) + protected label?: string; + + protected render(): TemplateResult | void { + if (!this.thumbnail) { + return; + } + + return html` { + // The view is not accessible from here, since this element is created + // from a string (see _buildEventContent below), so instead we emit an + // intermediate event that is caught by the timeline. + dispatchFrigateCardEvent(this, 'timeline-select', this.media_id); + }} + src="${this.thumbnail}" + title="${this.label || ''}" + aria-label="${this.label || ''}" + />`; + } + + static get styles(): CSSResultGroup { + return unsafeCSS(timelineEventStyle); + } +} + @customElement('frigate-card-timeline') export class FrigateCardTimeline extends LitElement { @property({ attribute: false }) @@ -38,6 +74,7 @@ export class FrigateCardTimeline extends LitElement { protected _timelineRef: Ref = createRef(); protected _timeline?: Timeline; + protected _boundTimelineSelectHandler = this._timelineSelectHandler.bind(this); /** * Master render method. @@ -70,6 +107,53 @@ export class FrigateCardTimeline extends LitElement { return html`
`; } + /** + * Component connected callback. + */ + connectedCallback(): void { + super.connectedCallback(); + document.addEventListener( + 'frigate-card:timeline-select', + this._boundTimelineSelectHandler, + ); + } + + /** + * Component disconnected callback. + */ + disconnectedCallback(): void { + document.removeEventListener( + 'frigate-card:timeline-select', + this._boundTimelineSelectHandler, + ); + super.disconnectedCallback(); + } + + protected _timelineSelectHandler(ev: Event): void { + const id = (ev as CustomEvent).detail; + const index = + this.view?.target?.children?.findIndex((item) => item.media_content_id == id) ?? + -1; + if (index >= 0) { + this.view + ?.evolve({ + view: 'clip', + childIndex: index, + }) + .dispatchChangeEvent(this); + } + } + + protected _buildEventContent(source: FrigateBrowseMediaSource): string { + return ` + + `; + } + protected _buildDataset(): DataSet { const items: FrigateCardTimelineData[] = []; @@ -77,8 +161,9 @@ export class FrigateCardTimeline extends LitElement { if (child.frigate) { const item = { id: child.media_content_id, - content: child.frigate.event.id, + content: this._buildEventContent(child), start: child.frigate.event.start_time * 1000, + selectable: false, }; if (child.frigate.event.end_time) { //item['end'] = child.frigate.event.end_time * 1000; @@ -94,6 +179,18 @@ export class FrigateCardTimeline extends LitElement { // Configuration for the Timeline const options = { minHeight: '300px', + margin: { + item: 75 + 10, + axis: 75 + 10, + }, + xss: { + disabled: false, + filterOptions: { + whiteList: { + 'frigate-card-timeline-event': ['thumbnail', 'label', 'media_id'], + }, + }, + }, }; // Create a Timeline diff --git a/src/scss/timeline-event.scss b/src/scss/timeline-event.scss new file mode 100644 index 00000000..3f974d39 --- /dev/null +++ b/src/scss/timeline-event.scss @@ -0,0 +1,18 @@ +:host { + display: block; + width: 100%; + height: 100%; +} + +img { + width: 75px; + height: 75px; + box-shadow: 0px 0px 20px 5px var(--primary-background-color); + display: block; + border-radius: 5px; + transition: transform 0.2s linear; +} + +img:hover { + transform: scale(1.04); +} diff --git a/src/scss/timeline.scss b/src/scss/timeline.scss index eb7759dd..423ac747 100644 --- a/src/scss/timeline.scss +++ b/src/scss/timeline.scss @@ -1,7 +1,19 @@ -@use "vis-timeline/dist/vis-timeline-graph2d.css"; +@use 'vis-timeline/dist/vis-timeline-graph2d.css'; :host { width: 100%; height: 100%; display: block; -} \ No newline at end of file +} + +.vis-item { + border-color: var(--primary-color); +} + +.vis-item.vis-box { + border-style: hidden; +} + +.vis-item .vis-item-content { + padding: 0px; +}