Add styling and interactivity.
This commit is contained in:
@@ -13,11 +13,13 @@ import { customElement, property } from 'lit/decorators.js';
|
|||||||
import { createRef, ref, Ref } from 'lit/directives/ref';
|
import { createRef, ref, Ref } from 'lit/directives/ref';
|
||||||
|
|
||||||
import { BrowseMediaUtil } from '../browse-media-util';
|
import { BrowseMediaUtil } from '../browse-media-util';
|
||||||
import { CameraConfig, ExtendedHomeAssistant } from '../types';
|
import { CameraConfig, ExtendedHomeAssistant, FrigateBrowseMediaSource } from '../types';
|
||||||
import { View } from '../view';
|
import { View } from '../view';
|
||||||
|
import { dispatchFrigateCardEvent } from '../common.js';
|
||||||
import { renderProgressIndicator } from './message';
|
import { renderProgressIndicator } from './message';
|
||||||
|
|
||||||
import timelineStyle from '../scss/timeline.scss';
|
import timelineStyle from '../scss/timeline.scss';
|
||||||
|
import timelineEventStyle from '../scss/timeline-event.scss';
|
||||||
|
|
||||||
interface FrigateCardTimelineData {
|
interface FrigateCardTimelineData {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -25,6 +27,40 @@ interface FrigateCardTimelineData {
|
|||||||
start: number;
|
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`<img
|
||||||
|
@click=${() => {
|
||||||
|
// 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')
|
@customElement('frigate-card-timeline')
|
||||||
export class FrigateCardTimeline extends LitElement {
|
export class FrigateCardTimeline extends LitElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
@@ -38,6 +74,7 @@ export class FrigateCardTimeline extends LitElement {
|
|||||||
|
|
||||||
protected _timelineRef: Ref<HTMLElement> = createRef();
|
protected _timelineRef: Ref<HTMLElement> = createRef();
|
||||||
protected _timeline?: Timeline;
|
protected _timeline?: Timeline;
|
||||||
|
protected _boundTimelineSelectHandler = this._timelineSelectHandler.bind(this);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Master render method.
|
* Master render method.
|
||||||
@@ -70,6 +107,53 @@ export class FrigateCardTimeline extends LitElement {
|
|||||||
return html` <div id="timeline" ${ref(this._timelineRef)}></div>`;
|
return html` <div id="timeline" ${ref(this._timelineRef)}></div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<string>).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 `
|
||||||
|
<frigate-card-timeline-event
|
||||||
|
media_id=${source.media_content_id}
|
||||||
|
thumbnail="${source.thumbnail}"
|
||||||
|
label="${source.title}"
|
||||||
|
>
|
||||||
|
</frigate-card-timeline-event>`;
|
||||||
|
}
|
||||||
|
|
||||||
protected _buildDataset(): DataSet<FrigateCardTimelineData> {
|
protected _buildDataset(): DataSet<FrigateCardTimelineData> {
|
||||||
const items: FrigateCardTimelineData[] = [];
|
const items: FrigateCardTimelineData[] = [];
|
||||||
|
|
||||||
@@ -77,8 +161,9 @@ export class FrigateCardTimeline extends LitElement {
|
|||||||
if (child.frigate) {
|
if (child.frigate) {
|
||||||
const item = {
|
const item = {
|
||||||
id: child.media_content_id,
|
id: child.media_content_id,
|
||||||
content: child.frigate.event.id,
|
content: this._buildEventContent(child),
|
||||||
start: child.frigate.event.start_time * 1000,
|
start: child.frigate.event.start_time * 1000,
|
||||||
|
selectable: false,
|
||||||
};
|
};
|
||||||
if (child.frigate.event.end_time) {
|
if (child.frigate.event.end_time) {
|
||||||
//item['end'] = child.frigate.event.end_time * 1000;
|
//item['end'] = child.frigate.event.end_time * 1000;
|
||||||
@@ -94,6 +179,18 @@ export class FrigateCardTimeline extends LitElement {
|
|||||||
// Configuration for the Timeline
|
// Configuration for the Timeline
|
||||||
const options = {
|
const options = {
|
||||||
minHeight: '300px',
|
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
|
// Create a Timeline
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
+13
-1
@@ -1,7 +1,19 @@
|
|||||||
@use "vis-timeline/dist/vis-timeline-graph2d.css";
|
@use 'vis-timeline/dist/vis-timeline-graph2d.css';
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vis-item {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vis-item.vis-box {
|
||||||
|
border-style: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vis-item .vis-item-content {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user