Add datepicker to timeline.
This commit is contained in:
@@ -31,6 +31,7 @@
|
|||||||
"home-assistant-js-websocket": "^8.0.0",
|
"home-assistant-js-websocket": "^8.0.0",
|
||||||
"keycharm": "^0.4.0",
|
"keycharm": "^0.4.0",
|
||||||
"lit": "^2.3.1",
|
"lit": "^2.3.1",
|
||||||
|
"lit-flatpickr": "^0.4.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"propagating-hammerjs": "^2.0.1",
|
"propagating-hammerjs": "^2.0.1",
|
||||||
|
|||||||
+3
-2
@@ -91,11 +91,12 @@ const config = {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
plugins: plugins,
|
plugins: plugins,
|
||||||
// These two files use this at the toplevel, which causes rollup warning
|
// These files use this at the toplevel, which causes rollup warning
|
||||||
// spam on build: `this` has been rewritten to `undefined`
|
// spam on build: `this` has been rewritten to `undefined`.
|
||||||
moduleContext: {
|
moduleContext: {
|
||||||
'./node_modules/@formatjs/intl-utils/lib/src/diff.js': 'window',
|
'./node_modules/@formatjs/intl-utils/lib/src/diff.js': 'window',
|
||||||
'./node_modules/@formatjs/intl-utils/lib/src/resolve-locale.js': 'window',
|
'./node_modules/@formatjs/intl-utils/lib/src/resolve-locale.js': 'window',
|
||||||
|
'./node_modules/flatpickr/dist/esm/index.js': 'window',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
||||||
|
import 'lit-flatpickr';
|
||||||
|
import { LitFlatpickr } from 'lit-flatpickr';
|
||||||
|
import { customElement } from 'lit/decorators.js';
|
||||||
|
import { createRef, ref, Ref } from 'lit/directives/ref.js';
|
||||||
|
import datePickerStyle from '../scss/date-picker.scss';
|
||||||
|
import { dispatchFrigateCardEvent } from '../utils/basic';
|
||||||
|
|
||||||
|
export interface DatePickerEvent {
|
||||||
|
date: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement('frigate-card-date-picker')
|
||||||
|
export class FrigateCardDatePicker extends LitElement {
|
||||||
|
protected _refInput: Ref<LitFlatpickr> = createRef();
|
||||||
|
|
||||||
|
public open(): void {
|
||||||
|
this._refInput.value?.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html` <lit-flatpickr
|
||||||
|
${ref(this._refInput)}
|
||||||
|
.onChange=${(dates: Date[]) => {
|
||||||
|
if (dates.length) {
|
||||||
|
// This is a single date picker, there should be only a single date.
|
||||||
|
dispatchFrigateCardEvent<DatePickerEvent>(this, 'date-picker:change', {
|
||||||
|
date: dates[0],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
></lit-flatpickr>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return unsafeCSS(datePickerStyle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'frigate-card-date-picker': FrigateCardDatePicker;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,6 +59,10 @@ import { ViewMediaClassifier } from '../view/media-classifier';
|
|||||||
import { rangesOverlap } from '../camera-manager/range';
|
import { rangesOverlap } from '../camera-manager/range';
|
||||||
import { View } from '../view/view';
|
import { View } from '../view/view';
|
||||||
import { MediaQuery } from '../camera-manager/types';
|
import { MediaQuery } from '../camera-manager/types';
|
||||||
|
import './date-picker.js';
|
||||||
|
import { DatePickerEvent, FrigateCardDatePicker } from './date-picker.js';
|
||||||
|
import startOfDay from 'date-fns/startOfDay';
|
||||||
|
import endOfDay from 'date-fns/endOfDay';
|
||||||
|
|
||||||
interface FrigateCardGroupData {
|
interface FrigateCardGroupData {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -191,7 +195,10 @@ export class FrigateCardTimelineCore extends LitElement {
|
|||||||
protected _locked = false;
|
protected _locked = false;
|
||||||
|
|
||||||
protected _targetBarVisible = false;
|
protected _targetBarVisible = false;
|
||||||
|
|
||||||
|
protected _refDatePicker: Ref<FrigateCardDatePicker> = createRef();
|
||||||
protected _refTimeline: Ref<HTMLElement> = createRef();
|
protected _refTimeline: Ref<HTMLElement> = createRef();
|
||||||
|
|
||||||
protected _timeline?: Timeline;
|
protected _timeline?: Timeline;
|
||||||
|
|
||||||
protected _timelineSource: TimelineDataSource | null = null;
|
protected _timelineSource: TimelineDataSource | null = null;
|
||||||
@@ -260,6 +267,10 @@ export class FrigateCardTimelineCore extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const capabilities = this.cameraManager?.getAggregateCameraCapabilities(cameraIDs);
|
const capabilities = this.cameraManager?.getAggregateCameraCapabilities(cameraIDs);
|
||||||
|
const lockTitle = this._locked
|
||||||
|
? localize('timeline.unlock')
|
||||||
|
: localize('timeline.lock');
|
||||||
|
|
||||||
return html` ${capabilities?.supportsTimeline
|
return html` ${capabilities?.supportsTimeline
|
||||||
? html` <div
|
? html` <div
|
||||||
@frigate-card:timeline:thumbnail-data-request=${this._handleThumbnailDataRequest.bind(
|
@frigate-card:timeline:thumbnail-data-request=${this._handleThumbnailDataRequest.bind(
|
||||||
@@ -268,20 +279,36 @@ export class FrigateCardTimelineCore extends LitElement {
|
|||||||
class="timeline"
|
class="timeline"
|
||||||
${ref(this._refTimeline)}
|
${ref(this._refTimeline)}
|
||||||
>
|
>
|
||||||
<ha-icon
|
<div class="timeline-tools">
|
||||||
class="lock"
|
<frigate-card-date-picker
|
||||||
.icon=${`mdi:${this._locked ? 'lock' : 'lock-open-variant'}`}
|
${ref(this._refDatePicker)}
|
||||||
@click=${() => {
|
@frigate-card:date-picker:change=${(ev: CustomEvent<DatePickerEvent>) => {
|
||||||
this._locked = !this._locked;
|
this._timeline?.setWindow(
|
||||||
}}
|
startOfDay(ev.detail.date),
|
||||||
aria-label="${this._locked
|
endOfDay(ev.detail.date),
|
||||||
? localize('timeline.unlock')
|
);
|
||||||
: localize('timeline.lock')}"
|
}}
|
||||||
title="${this._locked
|
>
|
||||||
? localize('timeline.unlock')
|
</frigate-card-date-picker>
|
||||||
: localize('timeline.lock')}"
|
<ha-icon
|
||||||
>
|
.icon=${`mdi:calendar-search`}
|
||||||
</ha-icon>
|
aria-label="${localize('timeline.select_date')}"
|
||||||
|
title="${localize('timeline.select_date')}"
|
||||||
|
@click=${() => {
|
||||||
|
this._refDatePicker.value?.open();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
</ha-icon>
|
||||||
|
<ha-icon
|
||||||
|
.icon=${`mdi:${this._locked ? 'lock' : 'lock-open-variant'}`}
|
||||||
|
@click=${() => {
|
||||||
|
this._locked = !this._locked;
|
||||||
|
}}
|
||||||
|
aria-label="${lockTitle}"
|
||||||
|
title="${lockTitle}"
|
||||||
|
>
|
||||||
|
</ha-icon>
|
||||||
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
: ''}`;
|
: ''}`;
|
||||||
}
|
}
|
||||||
@@ -896,9 +923,9 @@ export class FrigateCardTimelineCore extends LitElement {
|
|||||||
const mediaEndTime = media?.getEndTime();
|
const mediaEndTime = media?.getEndTime();
|
||||||
const mediaWindow: TimelineWindow | null =
|
const mediaWindow: TimelineWindow | null =
|
||||||
media && mediaStartTime
|
media && mediaStartTime
|
||||||
// If this media has no end time, it's just a "point" in time so the
|
? // If this media has no end time, it's just a "point" in time so the
|
||||||
// range effectively starts/ends at the same time.
|
// range effectively starts/ends at the same time.
|
||||||
? { start: mediaStartTime, end: mediaEndTime ?? mediaStartTime }
|
{ start: mediaStartTime, end: mediaEndTime ?? mediaStartTime }
|
||||||
: null;
|
: null;
|
||||||
const context = this.view.context?.timeline;
|
const context = this.view.context?.timeline;
|
||||||
|
|
||||||
|
|||||||
@@ -461,6 +461,7 @@
|
|||||||
},
|
},
|
||||||
"timeline": {
|
"timeline": {
|
||||||
"lock": "Lock timeline to a single event",
|
"lock": "Lock timeline to a single event",
|
||||||
|
"select_date": "Choose date",
|
||||||
"unlock": "Unlock timeline"
|
"unlock": "Unlock timeline"
|
||||||
},
|
},
|
||||||
"elements": {
|
"elements": {
|
||||||
|
|||||||
@@ -450,6 +450,7 @@
|
|||||||
},
|
},
|
||||||
"timeline": {
|
"timeline": {
|
||||||
"lock": "",
|
"lock": "",
|
||||||
|
"select_date": "",
|
||||||
"unlock": ""
|
"unlock": ""
|
||||||
},
|
},
|
||||||
"elements": {
|
"elements": {
|
||||||
|
|||||||
@@ -450,6 +450,7 @@
|
|||||||
},
|
},
|
||||||
"timeline": {
|
"timeline": {
|
||||||
"lock": "",
|
"lock": "",
|
||||||
|
"select_date": "",
|
||||||
"unlock": ""
|
"unlock": ""
|
||||||
},
|
},
|
||||||
"elements": {
|
"elements": {
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// The lit-flatpickr element itself is hidden as we only want the popup
|
||||||
|
// calender.
|
||||||
|
lit-flatpickr {
|
||||||
|
visibility: hidden;
|
||||||
|
width: 0px;
|
||||||
|
height: 0px;
|
||||||
|
}
|
||||||
@@ -141,11 +141,14 @@ div.vis-tooltip {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-icon.lock {
|
.timeline-tools {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 2px;
|
right: 2px;
|
||||||
bottom: 2px;
|
bottom: 2px;
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-tools ha-icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@@ -2407,6 +2407,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"flatpickr@npm:^4.6.13":
|
||||||
|
version: 4.6.13
|
||||||
|
resolution: "flatpickr@npm:4.6.13"
|
||||||
|
checksum: 2cca1b8dc974e7673b51174ee3f0679ae3a6b79525a0c53bf49b87455ef358354e64acfd60b312e44cef208aab9750a1a48df32a0914046a53de6d9403742304
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"flatted@npm:^3.1.0":
|
"flatted@npm:^3.1.0":
|
||||||
version: 3.2.7
|
version: 3.2.7
|
||||||
resolution: "flatted@npm:3.2.7"
|
resolution: "flatted@npm:3.2.7"
|
||||||
@@ -2460,6 +2467,7 @@ __metadata:
|
|||||||
home-assistant-js-websocket: ^8.0.0
|
home-assistant-js-websocket: ^8.0.0
|
||||||
keycharm: ^0.4.0
|
keycharm: ^0.4.0
|
||||||
lit: ^2.3.1
|
lit: ^2.3.1
|
||||||
|
lit-flatpickr: ^0.4.0
|
||||||
lodash-es: ^4.17.21
|
lodash-es: ^4.17.21
|
||||||
moment: ^2.29.4
|
moment: ^2.29.4
|
||||||
prettier: ^2.6.0
|
prettier: ^2.6.0
|
||||||
@@ -3327,6 +3335,17 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"lit-flatpickr@npm:^0.4.0":
|
||||||
|
version: 0.4.0
|
||||||
|
resolution: "lit-flatpickr@npm:0.4.0"
|
||||||
|
dependencies:
|
||||||
|
flatpickr: ^4.6.13
|
||||||
|
lit: ^2.0.0
|
||||||
|
tslib: ^1.11.0
|
||||||
|
checksum: 2141902346bf3f2d291e1c3369317bb494641da48a3f69dabb891b7d065e2505b268c584cbc1859c36763374b9880399b17d93721539b2dc7d90c58988dba471
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"lit-html@npm:^2.2.0, lit-html@npm:^2.6.0":
|
"lit-html@npm:^2.2.0, lit-html@npm:^2.6.0":
|
||||||
version: 2.6.1
|
version: 2.6.1
|
||||||
resolution: "lit-html@npm:2.6.1"
|
resolution: "lit-html@npm:2.6.1"
|
||||||
@@ -5160,7 +5179,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"tslib@npm:^1.8.1":
|
"tslib@npm:^1.11.0, tslib@npm:^1.8.1":
|
||||||
version: 1.14.1
|
version: 1.14.1
|
||||||
resolution: "tslib@npm:1.14.1"
|
resolution: "tslib@npm:1.14.1"
|
||||||
checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd
|
checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd
|
||||||
|
|||||||
Reference in New Issue
Block a user