Merge pull request #59 from dermotduffy/controls
Allow next/prev controls to be thumbnails, chevrons or off entirely
This commit is contained in:
@@ -72,6 +72,7 @@ lovelace:
|
|||||||
| ------------- | --------------------------------------------- | - |
|
| ------------- | --------------------------------------------- | - |
|
||||||
| `menu_mode` | `hidden-top` | The menu mode to show by default. See [menu modes](#menu-modes) below.|
|
| `menu_mode` | `hidden-top` | The menu mode to show by default. See [menu modes](#menu-modes) below.|
|
||||||
| `menu_buttons.{frigate, live, clips, snapshots, frigate_ui}` | `true` | Whether or not to show these builtin actions in the card menu. |
|
| `menu_buttons.{frigate, live, clips, snapshots, frigate_ui}` | `true` | Whether or not to show these builtin actions in the card menu. |
|
||||||
|
| `controls.nextprev` | `thumbnails` | When viewing media, what kind of controls to show to move to the previous/next media item. Acceptable values: `thumbnails`, `chevrons`, `none` . |
|
||||||
|
|
||||||
### Advanced
|
### Advanced
|
||||||
|
|
||||||
|
|||||||
+31
-3
@@ -6,7 +6,7 @@ import { HomeAssistant, LovelaceCardEditor, fireEvent } from 'custom-card-helper
|
|||||||
import { localize } from './localize/localize';
|
import { localize } from './localize/localize';
|
||||||
import type { FrigateCardConfig } from './types';
|
import type { FrigateCardConfig } from './types';
|
||||||
|
|
||||||
import frigate_card_editor_style from './frigate-hass-card-editor.scss';
|
import frigate_card_editor_style from './scss/editor.scss';
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
required: {
|
required: {
|
||||||
@@ -124,6 +124,14 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
frigate: localize('live_provider.frigate'),
|
frigate: localize('live_provider.frigate'),
|
||||||
webrtc: localize('live_provider.webrtc'),
|
webrtc: localize('live_provider.webrtc'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const controlsNextPrev = {
|
||||||
|
'': '',
|
||||||
|
thumbnails: localize('control.thumbnails'),
|
||||||
|
chevrons: localize('control.chevrons'),
|
||||||
|
none: localize('control.none'),
|
||||||
|
};
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="card-config">
|
<div class="card-config">
|
||||||
<div class="option" @click=${this._toggleOption} .option=${'required'}>
|
<div class="option" @click=${this._toggleOption} .option=${'required'}>
|
||||||
@@ -243,8 +251,28 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<paper-item .label="${key}"> ${menuModes[key]} </paper-item>
|
<paper-item .label="${key}"> ${menuModes[key]} </paper-item>
|
||||||
`;
|
`;
|
||||||
})}
|
})}
|
||||||
</paper-listbox> </paper-dropdown-menu
|
</paper-listbox>
|
||||||
><br />
|
</paper-dropdown-menu>
|
||||||
|
<br />
|
||||||
|
<paper-dropdown-menu
|
||||||
|
.label=${localize('control.nextprev')}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.configValue=${'controls.nextprev'}
|
||||||
|
>
|
||||||
|
<paper-listbox
|
||||||
|
slot="dropdown-content"
|
||||||
|
.selected=${Object.keys(controlsNextPrev).indexOf(
|
||||||
|
this._config?.controls?.nextprev || '',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
${Object.keys(controlsNextPrev).map((key) => {
|
||||||
|
return html`
|
||||||
|
<paper-item .label="${key}"> ${controlsNextPrev[key]} </paper-item>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-dropdown-menu>
|
||||||
|
<br />
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
.label=${localize('editor.show_button') +
|
.label=${localize('editor.show_button') +
|
||||||
': ' +
|
': ' +
|
||||||
|
|||||||
+63
-32
@@ -21,8 +21,8 @@ import {
|
|||||||
|
|
||||||
import './editor';
|
import './editor';
|
||||||
|
|
||||||
import frigate_card_style from './frigate-hass-card.scss';
|
import frigate_card_style from './scss/card.scss';
|
||||||
import frigate_card_menu_style from './frigate-hass-card-menu.scss';
|
import frigate_card_menu_style from './scss/menu.scss';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
MenuButton,
|
MenuButton,
|
||||||
@@ -722,6 +722,55 @@ export class FrigateCard extends LitElement {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render the next/previous controls.
|
||||||
|
protected _renderNextPreviousControls(
|
||||||
|
previous: boolean,
|
||||||
|
parent?: BrowseMediaSource,
|
||||||
|
targetChildIndex?: number,
|
||||||
|
neighbor?: BrowseMediaSource,
|
||||||
|
): TemplateResult {
|
||||||
|
if (!neighbor || this.config.controls?.nextprev === 'none') {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
const classes = {
|
||||||
|
'frigate-media-controls': true,
|
||||||
|
previous: previous,
|
||||||
|
next: !previous,
|
||||||
|
thumbnails: this.config.controls?.nextprev === "thumbnails",
|
||||||
|
chevrons: this.config.controls?.nextprev === "chevrons",
|
||||||
|
button: this.config.controls?.nextprev === "chevrons",
|
||||||
|
}
|
||||||
|
|
||||||
|
const clickChangeView = () => {
|
||||||
|
this._view = new View({
|
||||||
|
view: this._view.view,
|
||||||
|
target: parent,
|
||||||
|
childIndex: targetChildIndex,
|
||||||
|
previous: this._view,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.config.controls?.nextprev == 'chevrons') {
|
||||||
|
return html` <ha-icon-button
|
||||||
|
icon=${previous ? 'mdi:chevron-left' : 'mdi:chevron-right'}
|
||||||
|
class="${classMap(classes)}"
|
||||||
|
title=${neighbor.title}
|
||||||
|
@click=${clickChangeView}
|
||||||
|
></ha-icon-button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!neighbor.thumbnail) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
return html`<img
|
||||||
|
src="${neighbor.thumbnail}"
|
||||||
|
class="${classMap(classes)}"
|
||||||
|
title="${neighbor.title}"
|
||||||
|
@click=${clickChangeView}
|
||||||
|
/>`;
|
||||||
|
}
|
||||||
|
|
||||||
// Render the view for media.
|
// Render the view for media.
|
||||||
protected async _renderViewer(): Promise<TemplateResult> {
|
protected async _renderViewer(): Promise<TemplateResult> {
|
||||||
let autoplay = true;
|
let autoplay = true;
|
||||||
@@ -767,21 +816,12 @@ export class FrigateCard extends LitElement {
|
|||||||
const neighbors = this._getMediaNeighbors(parent, childIndex);
|
const neighbors = this._getMediaNeighbors(parent, childIndex);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${neighbors?.previous && neighbors.previous.thumbnail
|
${this._renderNextPreviousControls(
|
||||||
? html`<img
|
true,
|
||||||
src="${neighbors.previous.thumbnail}"
|
parent,
|
||||||
class="frigate-media-controls previous"
|
neighbors?.previousIndex ?? undefined,
|
||||||
title="${neighbors.previous.title}"
|
neighbors?.previous ?? undefined,
|
||||||
@click=${() => {
|
)}
|
||||||
this._view = new View({
|
|
||||||
view: this._view.view,
|
|
||||||
target: parent || undefined,
|
|
||||||
childIndex: neighbors.previousIndex ?? undefined,
|
|
||||||
previous: this._view,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>`
|
|
||||||
: ``}
|
|
||||||
${this._view.is('clip')
|
${this._view.is('clip')
|
||||||
? resolvedMedia?.mime_type.toLowerCase() == 'application/x-mpegurl'
|
? resolvedMedia?.mime_type.toLowerCase() == 'application/x-mpegurl'
|
||||||
? html`<ha-hls-player
|
? html`<ha-hls-player
|
||||||
@@ -831,21 +871,12 @@ export class FrigateCard extends LitElement {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>`}
|
/>`}
|
||||||
${neighbors?.next && neighbors.next.thumbnail
|
${this._renderNextPreviousControls(
|
||||||
? html`<img
|
false,
|
||||||
src="${neighbors.next.thumbnail}"
|
parent,
|
||||||
class="frigate-media-controls next"
|
neighbors?.nextIndex ?? undefined,
|
||||||
title="${neighbors.next.title}"
|
neighbors?.next ?? undefined,
|
||||||
@click=${() => {
|
)}
|
||||||
this._view = new View({
|
|
||||||
view: this._view.view,
|
|
||||||
target: parent || undefined,
|
|
||||||
childIndex: neighbors.nextIndex ?? undefined,
|
|
||||||
previous: this._view,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>`
|
|
||||||
: ``}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,12 @@
|
|||||||
"snapshot": "Latest Snapshot",
|
"snapshot": "Latest Snapshot",
|
||||||
"frigate_ui": "Frigate User Interface"
|
"frigate_ui": "Frigate User Interface"
|
||||||
},
|
},
|
||||||
|
"control": {
|
||||||
|
"nextprev": "Media Next & Previous Controls",
|
||||||
|
"thumbnails": "Thumbnails",
|
||||||
|
"chevrons": "Chevrons",
|
||||||
|
"none": "None"
|
||||||
|
},
|
||||||
"menu_mode": {
|
"menu_mode": {
|
||||||
"none": "No menu",
|
"none": "No menu",
|
||||||
"hidden-top": "Hidden Top",
|
"hidden-top": "Hidden Top",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@use "@material/image-list/mdc-image-list";
|
@use "@material/image-list/mdc-image-list";
|
||||||
@use "@material/image-list";
|
@use "@material/image-list";
|
||||||
|
@use './common.scss';
|
||||||
|
|
||||||
.frigate-card-contents {
|
.frigate-card-contents {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -87,30 +88,36 @@ webrtc-camera ha-card {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
opacity: 0.8;
|
}
|
||||||
|
.frigate-media-controls.previous {
|
||||||
|
left: 45px;
|
||||||
|
}
|
||||||
|
.frigate-media-controls.next {
|
||||||
|
right: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frigate-media-controls.chevrons {
|
||||||
|
top: calc(50% - (40px / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
.frigate-media-controls.thumbnails {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
top: calc(50% - (48px / 2));
|
top: calc(50% - (48px / 2));
|
||||||
box-shadow: 0px 0px 30px 1px black;
|
box-shadow: 0px 0px 30px 1px black;
|
||||||
transition: all .2s ease;
|
transition: all .2s ease;
|
||||||
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
.frigate-media-controls.thumbnails:hover {
|
||||||
.frigate-media-controls:hover {
|
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
height: 72px;
|
height: 72px;
|
||||||
top: calc(50% - (72px / 2));
|
top: calc(50% - (72px / 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
.frigate-media-controls.previous {
|
.frigate-media-controls.previous.thumbnails:hover {
|
||||||
left: 45px;
|
|
||||||
}
|
|
||||||
.frigate-media-controls.previous:hover {
|
|
||||||
left: 33px;
|
left: 33px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.frigate-media-controls.next {
|
.frigate-media-controls.next.thumbnails:hover {
|
||||||
right: 45px;
|
|
||||||
}
|
|
||||||
.frigate-media-controls.next:hover {
|
|
||||||
right: 33px;
|
right: 33px;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
ha-icon-button.button {
|
||||||
|
z-index: 10;
|
||||||
|
color: var(--secondary-color, white);
|
||||||
|
opacity: 0.8;
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 3px;
|
||||||
|
--mdc-icon-button-size: 40px;
|
||||||
|
/* Buttons can always be clicked */
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-icon-button.button.emphasize {
|
||||||
|
color: var(--primary-color, white);
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@use './common.scss';
|
||||||
|
|
||||||
.frigate-card-menu {
|
.frigate-card-menu {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
height: 46px;
|
height: 46px;
|
||||||
@@ -36,21 +38,3 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
background: var(--secondary-background-color);
|
background: var(--secondary-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-icon-button.button {
|
|
||||||
position: relative;
|
|
||||||
z-index: 10;
|
|
||||||
color: var(--secondary-color, white);
|
|
||||||
opacity: 0.8;
|
|
||||||
background-color: rgba(0, 0, 0, 0.6);
|
|
||||||
border-radius: 50%;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 3px;
|
|
||||||
--mdc-icon-button-size: 40px;
|
|
||||||
/* Buttons can always be clicked */
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-icon-button.button.emphasize {
|
|
||||||
color: var(--primary-color, white);
|
|
||||||
}
|
|
||||||
@@ -74,6 +74,9 @@ export const frigateCardConfigSchema = z.object({
|
|||||||
show: z.boolean().default(true),
|
show: z.boolean().default(true),
|
||||||
icon: z.string().optional(),
|
icon: z.string().optional(),
|
||||||
}).array().optional(),
|
}).array().optional(),
|
||||||
|
controls: z.object({
|
||||||
|
nextprev: z.enum(['thumbnails', 'chevrons', 'none']).default('thumbnails'),
|
||||||
|
}).optional(),
|
||||||
|
|
||||||
// Stock lovelace card config.
|
// Stock lovelace card config.
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
|
|||||||
Reference in New Issue
Block a user