Add drawer component and support side thumbnails.

This commit is contained in:
Dermot Duffy
2022-03-20 18:07:48 -07:00
parent 3d3967318b
commit 304abcd82b
8 changed files with 149 additions and 7 deletions
+3 -2
View File
@@ -24,16 +24,17 @@
"@types/bluebird": "^3.5.36",
"component-emitter": "^1.3.0",
"crypto": "^1.0.1",
"custom-card-helpers": "^1.8.0",
"custom-card-helpers": "^1.9.0",
"embla-carousel": "^6.1.1",
"home-assistant-js-websocket": "^6.1.1",
"lit": "^2.2.1",
"keycharm": "^0.4.0",
"lit": "^2.2.1",
"lodash-es": "^4.17.21",
"moment": "^2.29.1",
"propagating-hammerjs": "^2.0.1",
"quick-lru": "^6.1.0",
"screenfull": "^6.0.1",
"side-drawer": "^3.0.0",
"ts-toolbelt": "^9.6.0",
"uuid": "^8.3.2",
"vis-data": "^7.1.3",
+12 -1
View File
@@ -1,4 +1,6 @@
import { CSSResultGroup, LitElement, unsafeCSS, PropertyValues } from 'lit';
import { property } from 'lit/decorators.js';
import EmblaCarousel, {
EmblaCarouselType,
EmblaOptionsType,
@@ -15,6 +17,9 @@ export interface CarouselSelect {
}
export class FrigateCardCarousel extends LitElement {
@property({ attribute: true, reflect: true })
public direction: 'vertical' | 'horizontal' = 'horizontal';
protected _carousel?: EmblaCarouselType;
protected _plugins: Record<string, EmblaPluginType> = {};
@@ -98,7 +103,13 @@ export class FrigateCardCarousel extends LitElement {
return acc;
}, {});
this._carousel = EmblaCarousel(carouselNode, this._getOptions(), plugins);
this._carousel = EmblaCarousel(
carouselNode,
{
axis: this.direction == 'horizontal' ? 'x' : 'y',
...this._getOptions()
},
plugins);
this._carousel.on('init', () => dispatchFrigateCardEvent(this, 'carousel:init'));
this._carousel.on('select', () => {
const selected = this.carouselSelected();
+63
View File
@@ -0,0 +1,63 @@
import {
CSSResultGroup,
LitElement,
TemplateResult,
html,
unsafeCSS,
PropertyValues,
} from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { createRef, ref, Ref } from 'lit/directives/ref.js';
import 'side-drawer';
import drawerStyle from '../scss/drawer.scss';
import drawerInjectStyle from '../scss/drawer-inject.scss';
@customElement('frigate-card-drawer')
export class FrigateCardDrawer extends LitElement {
@property({ attribute: true, reflect: true })
public location: 'left' | 'right' = 'left';
/**
* Set the timeline configuration.
*/
@property({ type: Boolean, reflect: true, attribute: true })
set open(open: boolean) {
if (this._drawerRef.value) {
const old = this._drawerRef.value.open;
this._drawerRef.value.open = open;
this.requestUpdate('open', old);
}
}
get open(): boolean {
return this._drawerRef.value?.open ?? false;
}
protected _drawerRef: Ref<HTMLElement & { open: boolean }> = createRef();
/**
* Called on the first update.
* @param changedProps The changed properties.
*/
protected firstUpdated(changedProps: PropertyValues): void {
super.firstUpdated(changedProps);
// The `side-drawer` component (and the material drawer for that matter)
// only do fixed drawers (i.e. a drawer for the whole viewport). Hackily
// override the style to customize the drawer to be absolute within the div.
const style = document.createElement('style');
style.innerHTML = drawerInjectStyle;
this._drawerRef.value?.shadowRoot?.appendChild(style);
}
protected render(): TemplateResult {
return html` <side-drawer location="${this.location}" ${ref(this._drawerRef)}>
<slot></slot>
</side-drawer>`;
}
static get styles(): CSSResultGroup {
return unsafeCSS(drawerStyle);
}
}
+17 -3
View File
@@ -1,11 +1,14 @@
import { BrowseMediaUtil } from '../browse-media-util.js';
import { CSSResultGroup, TemplateResult, html, unsafeCSS } from 'lit';
import { CSSResultGroup, TemplateResult, html, unsafeCSS, PropertyValues } from 'lit';
import { EmblaOptionsType } from 'embla-carousel';
import { customElement, property } from 'lit/decorators.js';
import type { FrigateBrowseMediaSource, ThumbnailsControlConfig } from '../types.js';
import { FrigateCardCarousel } from './carousel.js';
import { dispatchFrigateCardEvent, stopEventFromActivatingCardWideActions } from '../common.js';
import {
dispatchFrigateCardEvent,
stopEventFromActivatingCardWideActions,
} from '../common.js';
import thumbnailCarouselStyle from '../scss/thumbnail-carousel.scss';
@@ -18,7 +21,7 @@ export interface ThumbnailCarouselTap {
@customElement('frigate-card-thumbnail-carousel')
export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
@property({ attribute: false })
protected target?: FrigateBrowseMediaSource;
public target?: FrigateBrowseMediaSource;
protected _tapSelected?;
@@ -90,6 +93,17 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
return slides;
}
/**
* The updated lifecycle callback for this element.
* @param changedProperties The properties that were changed in this render.
*/
updated(changedProperties: PropertyValues): void {
if (this._carousel && changedProperties.has('target')) {
this._destroyCarousel();
}
super.updated(changedProperties);
}
/**
* Render a given thumbnail.
* @param mediaToRender The media item to render.
+14 -1
View File
@@ -23,11 +23,19 @@ img,video {
width: 100%;
height: 100%;
flex-direction: column;
user-select: none;
-webkit-touch-callout: none;
-khtml-user-select: none;
-webkit-tap-highlight-color: transparent;
}
:host([direction="vertical"]) .embla__container {
flex-direction: column;
}
:host([direction="horizontal"]) .embla__container {
flex-direction: row;
}
.embla__viewport {
width: 100%;
@@ -50,9 +58,14 @@ img,video {
.embla__slide {
position: relative;
height: 100%;
margin-right: 5px;
overflow: visible;
}
:host([direction="vertical"]) .embla__slide {
margin-bottom: 5px;
}
:host([direction="horizontal"]) .embla__slide {
margin-right: 5px;
}
.embla__slide img,video {
// Letterbox media. <frigate-card-ha-hls-player> has similar added directly in
// its element.
+34
View File
@@ -0,0 +1,34 @@
:host {
// Drawer width sizes to contents.
width: unset;
};
#d, #fs {
// Override width/height to be 100% instead of 100v[wh].
height: 100%;
// Position absolutely.
position: absolute;
}
:host([location=right]) #d {
// Position to the right.
left: unset;
right: 0;
transform: translateX(100%);
};
:host([location=right][open]) #d {
transform: none;
box-shadow: 0px 0px 25px 0px rgba(0, 0, 0, 0.5);
}
#fs {
width: 100%;
inset: 0;
}
#ifs {
// Override width/height to be 100% instead of 100v[wh].
height: 100%;
};
+3
View File
@@ -0,0 +1,3 @@
side-drawer {
background-color: var(--card-background-color);
}
+3
View File
@@ -6,6 +6,9 @@
display: block;
background-color: var(--card-background-color);
padding-bottom: 5px;
// So that absolute sidedrawer is relative to this host.
position: relative;
}
div.timeline {