Allow the carousel 'slide' animation to be disabled.
This commit is contained in:
@@ -195,6 +195,7 @@ live:
|
|||||||
| `lazy_load` | `true` | :heavy_multiplication_x: | Whether or not to lazily load cameras in the camera carousel. Setting this will `false` will cause all cameras to load simultaneously when the `live` carousel is opened (or cause all cameras to load continually if both `lazy_load` and `preload` are `true`). This will result in a smoother carousel experience at a cost of (potentially) a substantial amount of continually streamed data. |
|
| `lazy_load` | `true` | :heavy_multiplication_x: | Whether or not to lazily load cameras in the camera carousel. Setting this will `false` will cause all cameras to load simultaneously when the `live` carousel is opened (or cause all cameras to load continually if both `lazy_load` and `preload` are `true`). This will result in a smoother carousel experience at a cost of (potentially) a substantial amount of continually streamed data. |
|
||||||
| `lazy_unload` | `false` | :heavy_multiplication_x: | Whether or not to lazily **un**load lazyily-loaded cameras in the camera carousel, or just leave the camera paused. Setting this to `true` will cause cameras to be entirely unloaded when they are no longer visible (either because the carousel has scrolled past them, or because the document has been marked hidden/inactive by the browser). This will cause a reloading delay on revisiting that camera in the carousel but will save the streaming network resources that are otherwise consumed. This option has no effect if `lazy_load` is false. |
|
| `lazy_unload` | `false` | :heavy_multiplication_x: | Whether or not to lazily **un**load lazyily-loaded cameras in the camera carousel, or just leave the camera paused. Setting this to `true` will cause cameras to be entirely unloaded when they are no longer visible (either because the carousel has scrolled past them, or because the document has been marked hidden/inactive by the browser). This will cause a reloading delay on revisiting that camera in the carousel but will save the streaming network resources that are otherwise consumed. This option has no effect if `lazy_load` is false. |
|
||||||
| `draggable` | `true` | :heavy_multiplication_x: | Whether or not the live carousel can be dragged left or right, via touch/swipe and mouse dragging. |
|
| `draggable` | `true` | :heavy_multiplication_x: | Whether or not the live carousel can be dragged left or right, via touch/swipe and mouse dragging. |
|
||||||
|
| `transition_effect` | `slide` | :heavy_multiplication_x: | Effect to apply as a transition between live cameras. Accepted values: `slide` or `none`. |
|
||||||
| `provider` | `frigate` | :white_check_mark: | The means through which the live camera view is displayed. See [Live Provider](#live-provider) below.|
|
| `provider` | `frigate` | :white_check_mark: | The means through which the live camera view is displayed. See [Live Provider](#live-provider) below.|
|
||||||
| `actions` | | :white_check_mark: | Actions to use for the `live` view. See [actions](#actions) below.|
|
| `actions` | | :white_check_mark: | Actions to use for the `live` view. See [actions](#actions) below.|
|
||||||
| `controls` | | :white_check_mark: | Configuration for the `live` view controls. See below. |
|
| `controls` | | :white_check_mark: | Configuration for the `live` view controls. See below. |
|
||||||
@@ -303,6 +304,7 @@ event_viewer:
|
|||||||
| `auto_unmute` | `false` | :heavy_multiplication_x: | Whether or not to automatically unmute clips. Note that some browsers will not allow automated unmute until the user has interacted with the page in some way -- if the user has not then the browser may pause the media instead. |
|
| `auto_unmute` | `false` | :heavy_multiplication_x: | Whether or not to automatically unmute clips. Note that some browsers will not allow automated unmute until the user has interacted with the page in some way -- if the user has not then the browser may pause the media instead. |
|
||||||
| `lazy_load` | `true` | :heavy_multiplication_x: | Whether or not to lazily load media in the event viewer carousel. Setting this will false will fetch all media immediately which may make the carousel experience smoother at a cost of (potentially) a substantial number of simultaneous media fetches on load. |
|
| `lazy_load` | `true` | :heavy_multiplication_x: | Whether or not to lazily load media in the event viewer carousel. Setting this will false will fetch all media immediately which may make the carousel experience smoother at a cost of (potentially) a substantial number of simultaneous media fetches on load. |
|
||||||
| `draggable` | `true` | :heavy_multiplication_x: | Whether or not the event viewer carousel can be dragged left or right, via touch/swipe and mouse dragging. |
|
| `draggable` | `true` | :heavy_multiplication_x: | Whether or not the event viewer carousel can be dragged left or right, via touch/swipe and mouse dragging. |
|
||||||
|
| `transition_effect` | `slide` | :heavy_multiplication_x: | Effect to apply as a transition between event media. Accepted values: `slide` or `none`. |
|
||||||
| `controls` | | :heavy_multiplication_x: | Configuration for the event viewer. See below. |
|
| `controls` | | :heavy_multiplication_x: | Configuration for the event viewer. See below. |
|
||||||
| `actions` | | :heavy_multiplication_x: | Actions to use for all views that use the `event_viewer` (e.g. `clip`, `snapshot`). See [actions](#actions) below.|
|
| `actions` | | :heavy_multiplication_x: | Actions to use for all views that use the `event_viewer` (e.g. `clip`, `snapshot`). See [actions](#actions) below.|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import EmblaCarousel, {
|
|||||||
EmblaPluginType,
|
EmblaPluginType,
|
||||||
} from 'embla-carousel';
|
} from 'embla-carousel';
|
||||||
|
|
||||||
|
import { TransitionEffect } from '../types';
|
||||||
import { dispatchFrigateCardEvent } from '../common';
|
import { dispatchFrigateCardEvent } from '../common';
|
||||||
|
|
||||||
import carouselStyle from '../scss/carousel.scss';
|
import carouselStyle from '../scss/carousel.scss';
|
||||||
@@ -22,7 +23,7 @@ export class FrigateCardCarousel extends LitElement {
|
|||||||
* @param index Slide number.
|
* @param index Slide number.
|
||||||
*/
|
*/
|
||||||
carouselScrollTo(index: number): void {
|
carouselScrollTo(index: number): void {
|
||||||
this._carousel?.scrollTo(index);
|
this._carousel?.scrollTo(index, this._getTransitionEffect() === 'none');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,6 +51,14 @@ export class FrigateCardCarousel extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the transition effect to use.
|
||||||
|
* @returns An TransitionEffect object.
|
||||||
|
*/
|
||||||
|
protected _getTransitionEffect(): TransitionEffect | undefined {
|
||||||
|
return 'slide';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Embla options to use.
|
* Get the Embla options to use.
|
||||||
* @returns An EmblaOptionsType object or undefined for no options.
|
* @returns An EmblaOptionsType object or undefined for no options.
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
FrigateCardMediaPlayer,
|
FrigateCardMediaPlayer,
|
||||||
LiveOverrides,
|
LiveOverrides,
|
||||||
LiveProvider,
|
LiveProvider,
|
||||||
|
TransitionEffect,
|
||||||
frigateCardConfigDefaults,
|
frigateCardConfigDefaults,
|
||||||
} from '../types.js';
|
} from '../types.js';
|
||||||
import { EmblaOptionsType, EmblaPluginType } from 'embla-carousel';
|
import { EmblaOptionsType, EmblaPluginType } from 'embla-carousel';
|
||||||
@@ -314,6 +315,14 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the transition effect to use.
|
||||||
|
* @returns An TransitionEffect object.
|
||||||
|
*/
|
||||||
|
protected _getTransitionEffect(): TransitionEffect | undefined {
|
||||||
|
return this.liveConfig?.transition_effect;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Embla options to use.
|
* Get the Embla options to use.
|
||||||
* @returns An EmblaOptionsType object or undefined for no options.
|
* @returns An EmblaOptionsType object or undefined for no options.
|
||||||
|
|||||||
@@ -191,9 +191,9 @@ export class FrigateCardMediaCarousel extends FrigateCardCarousel {
|
|||||||
*/
|
*/
|
||||||
protected _nextPreviousHandler(direction: 'previous' | 'next'): void {
|
protected _nextPreviousHandler(direction: 'previous' | 'next'): void {
|
||||||
if (direction == 'previous') {
|
if (direction == 'previous') {
|
||||||
this._carousel?.scrollPrev();
|
this._carousel?.scrollPrev(this._getTransitionEffect() === 'none');
|
||||||
} else if (direction == 'next') {
|
} else if (direction == 'next') {
|
||||||
this._carousel?.scrollNext();
|
this._carousel?.scrollNext(this._getTransitionEffect() === 'none');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import type {
|
|||||||
CameraConfig,
|
CameraConfig,
|
||||||
ExtendedHomeAssistant,
|
ExtendedHomeAssistant,
|
||||||
MediaShowInfo,
|
MediaShowInfo,
|
||||||
|
TransitionEffect,
|
||||||
ViewerConfig,
|
ViewerConfig,
|
||||||
} from '../types.js';
|
} from '../types.js';
|
||||||
import { FrigateCardMediaCarousel, IMG_EMPTY } from './media-carousel.js';
|
import { FrigateCardMediaCarousel, IMG_EMPTY } from './media-carousel.js';
|
||||||
@@ -302,6 +303,14 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
|||||||
return slideIndex !== undefined ? Number(slideIndex) : undefined;
|
return slideIndex !== undefined ? Number(slideIndex) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the transition effect to use.
|
||||||
|
* @returns An TransitionEffect object.
|
||||||
|
*/
|
||||||
|
protected _getTransitionEffect(): TransitionEffect | undefined {
|
||||||
|
return this.viewerConfig?.transition_effect;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Embla options to use.
|
* Get the Embla options to use.
|
||||||
* @returns An EmblaOptionsType object or undefined for no options.
|
* @returns An EmblaOptionsType object or undefined for no options.
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ export const CONF_EVENT_VIEWER_AUTO_PLAY = `${CONF_EVENT_VIEWER}.auto_play` as c
|
|||||||
export const CONF_EVENT_VIEWER_AUTO_UNMUTE = `${CONF_EVENT_VIEWER}.auto_unmute` as const;
|
export const CONF_EVENT_VIEWER_AUTO_UNMUTE = `${CONF_EVENT_VIEWER}.auto_unmute` as const;
|
||||||
export const CONF_EVENT_VIEWER_DRAGGABLE = `${CONF_EVENT_VIEWER}.draggable` as const;
|
export const CONF_EVENT_VIEWER_DRAGGABLE = `${CONF_EVENT_VIEWER}.draggable` as const;
|
||||||
export const CONF_EVENT_VIEWER_LAZY_LOAD = `${CONF_EVENT_VIEWER}.lazy_load` as const;
|
export const CONF_EVENT_VIEWER_LAZY_LOAD = `${CONF_EVENT_VIEWER}.lazy_load` as const;
|
||||||
|
export const CONF_EVENT_VIEWER_TRANSITION_EFFECT =
|
||||||
|
`${CONF_EVENT_VIEWER}.transition_effect` as const;
|
||||||
export const CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE =
|
export const CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE =
|
||||||
`${CONF_EVENT_VIEWER}.controls.next_previous.style` as const;
|
`${CONF_EVENT_VIEWER}.controls.next_previous.style` as const;
|
||||||
export const CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_SIZE =
|
export const CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_SIZE =
|
||||||
@@ -70,6 +72,7 @@ export const CONF_LIVE_JSMPEG = `${CONF_LIVE}.jsmpeg` as const;
|
|||||||
export const CONF_LIVE_LAZY_LOAD = `${CONF_LIVE}.lazy_load` as const;
|
export const CONF_LIVE_LAZY_LOAD = `${CONF_LIVE}.lazy_load` as const;
|
||||||
export const CONF_LIVE_LAZY_UNLOAD = `${CONF_LIVE}.lazy_unload` as const;
|
export const CONF_LIVE_LAZY_UNLOAD = `${CONF_LIVE}.lazy_unload` as const;
|
||||||
export const CONF_LIVE_PRELOAD = `${CONF_LIVE}.preload` as const;
|
export const CONF_LIVE_PRELOAD = `${CONF_LIVE}.preload` as const;
|
||||||
|
export const CONF_LIVE_TRANSITION_EFFECT = `${CONF_LIVE}.transition_effect` as const;
|
||||||
export const CONF_LIVE_WEBRTC = `${CONF_LIVE}.webrtc` as const;
|
export const CONF_LIVE_WEBRTC = `${CONF_LIVE}.webrtc` as const;
|
||||||
export const CONF_LIVE_WEBRTC_ENTITY = `${CONF_LIVE_WEBRTC}.entity` as const;
|
export const CONF_LIVE_WEBRTC_ENTITY = `${CONF_LIVE_WEBRTC}.entity` as const;
|
||||||
export const CONF_LIVE_WEBRTC_URL = `${CONF_LIVE_WEBRTC}.url` as const;
|
export const CONF_LIVE_WEBRTC_URL = `${CONF_LIVE_WEBRTC}.url` as const;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import {
|
|||||||
CONF_EVENT_VIEWER_CONTROLS_TITLE_MODE,
|
CONF_EVENT_VIEWER_CONTROLS_TITLE_MODE,
|
||||||
CONF_EVENT_VIEWER_DRAGGABLE,
|
CONF_EVENT_VIEWER_DRAGGABLE,
|
||||||
CONF_EVENT_VIEWER_LAZY_LOAD,
|
CONF_EVENT_VIEWER_LAZY_LOAD,
|
||||||
|
CONF_EVENT_VIEWER_TRANSITION_EFFECT,
|
||||||
CONF_IMAGE_REFRESH_SECONDS,
|
CONF_IMAGE_REFRESH_SECONDS,
|
||||||
CONF_IMAGE_SRC,
|
CONF_IMAGE_SRC,
|
||||||
CONF_LIVE_AUTO_UNMUTE,
|
CONF_LIVE_AUTO_UNMUTE,
|
||||||
@@ -52,6 +53,7 @@ import {
|
|||||||
CONF_LIVE_LAZY_LOAD,
|
CONF_LIVE_LAZY_LOAD,
|
||||||
CONF_LIVE_LAZY_UNLOAD,
|
CONF_LIVE_LAZY_UNLOAD,
|
||||||
CONF_LIVE_PRELOAD,
|
CONF_LIVE_PRELOAD,
|
||||||
|
CONF_LIVE_TRANSITION_EFFECT,
|
||||||
CONF_MENU_BUTTONS_CLIPS,
|
CONF_MENU_BUTTONS_CLIPS,
|
||||||
CONF_MENU_BUTTONS_FRIGATE,
|
CONF_MENU_BUTTONS_FRIGATE,
|
||||||
CONF_MENU_BUTTONS_FRIGATE_DOWNLOAD,
|
CONF_MENU_BUTTONS_FRIGATE_DOWNLOAD,
|
||||||
@@ -266,6 +268,12 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
protected _transitionEffects = new Map([
|
||||||
|
['', ''],
|
||||||
|
['none', localize('config.event_viewer.transition_effects.none')],
|
||||||
|
['slide', localize('config.event_viewer.transition_effects.slide')],
|
||||||
|
]);
|
||||||
|
|
||||||
public setConfig(config: RawFrigateCardConfig): void {
|
public setConfig(config: RawFrigateCardConfig): void {
|
||||||
// Note: This does not use Zod to parse the configuration, so it may be
|
// Note: This does not use Zod to parse the configuration, so it may be
|
||||||
// partially or completely invalid. It's more useful to have a partially
|
// partially or completely invalid. It's more useful to have a partially
|
||||||
@@ -809,6 +817,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
CONF_LIVE_CONTROLS_TITLE_DURATION_SECONDS,
|
CONF_LIVE_CONTROLS_TITLE_DURATION_SECONDS,
|
||||||
'number',
|
'number',
|
||||||
)}
|
)}
|
||||||
|
${this._renderDropdown(
|
||||||
|
CONF_LIVE_TRANSITION_EFFECT,
|
||||||
|
this._transitionEffects,
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ''}
|
: ''}
|
||||||
@@ -861,6 +873,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
CONF_EVENT_VIEWER_CONTROLS_TITLE_DURATION_SECONDS,
|
CONF_EVENT_VIEWER_CONTROLS_TITLE_DURATION_SECONDS,
|
||||||
'number',
|
'number',
|
||||||
)}
|
)}
|
||||||
|
${this._renderDropdown(
|
||||||
|
CONF_EVENT_VIEWER_TRANSITION_EFFECT,
|
||||||
|
this._transitionEffects,
|
||||||
|
)}
|
||||||
</div>`
|
</div>`
|
||||||
: ''}
|
: ''}
|
||||||
${this._renderOptionSetHeader('image')}
|
${this._renderOptionSetHeader('image')}
|
||||||
|
|||||||
@@ -57,6 +57,11 @@
|
|||||||
"auto_unmute": "Automatically unmute media",
|
"auto_unmute": "Automatically unmute media",
|
||||||
"draggable": "Event Viewer can be dragged/swiped",
|
"draggable": "Event Viewer can be dragged/swiped",
|
||||||
"lazy_load": "Event Viewer media is lazily loaded in carousel",
|
"lazy_load": "Event Viewer media is lazily loaded in carousel",
|
||||||
|
"transition_effect": "Event Viewer transition effect",
|
||||||
|
"transition_effects": {
|
||||||
|
"none": "No transition",
|
||||||
|
"slide": "Slide transition"
|
||||||
|
},
|
||||||
"controls": {
|
"controls": {
|
||||||
"next_previous": {
|
"next_previous": {
|
||||||
"style": "Event Viewer next & previous control style",
|
"style": "Event Viewer next & previous control style",
|
||||||
@@ -95,6 +100,7 @@
|
|||||||
"lazy_load": "Live cameras are lazily loaded",
|
"lazy_load": "Live cameras are lazily loaded",
|
||||||
"lazy_unload": "Live cameras are lazily unloaded",
|
"lazy_unload": "Live cameras are lazily unloaded",
|
||||||
"auto_unmute": "Automatically unmute live cameras",
|
"auto_unmute": "Automatically unmute live cameras",
|
||||||
|
"transition_effect": "Live camera transition effect",
|
||||||
"controls": {
|
"controls": {
|
||||||
"next_previous": {
|
"next_previous": {
|
||||||
"style": "Live view next & previous control style",
|
"style": "Live view next & previous control style",
|
||||||
|
|||||||
@@ -436,6 +436,12 @@ const nextPreviousControlConfigSchema = z.object({
|
|||||||
});
|
});
|
||||||
export type NextPreviousControlConfig = z.infer<typeof nextPreviousControlConfigSchema>;
|
export type NextPreviousControlConfig = z.infer<typeof nextPreviousControlConfigSchema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carousel transition effect configuration.
|
||||||
|
*/
|
||||||
|
const transitionEffectConfigSchema = z.enum(['none', 'slide']);
|
||||||
|
export type TransitionEffect = z.infer<typeof transitionEffectConfigSchema>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Title Control configuration section.
|
* Title Control configuration section.
|
||||||
*/
|
*/
|
||||||
@@ -460,6 +466,7 @@ const liveConfigDefault = {
|
|||||||
lazy_load: true,
|
lazy_load: true,
|
||||||
lazy_unload: false,
|
lazy_unload: false,
|
||||||
draggable: true,
|
draggable: true,
|
||||||
|
transition_effect: 'slide' as const,
|
||||||
controls: {
|
controls: {
|
||||||
next_previous: {
|
next_previous: {
|
||||||
size: '48px',
|
size: '48px',
|
||||||
@@ -556,6 +563,7 @@ const liveConfigSchema = liveOverridableConfigSchema
|
|||||||
lazy_load: z.boolean().default(liveConfigDefault.lazy_load),
|
lazy_load: z.boolean().default(liveConfigDefault.lazy_load),
|
||||||
lazy_unload: z.boolean().default(liveConfigDefault.lazy_unload),
|
lazy_unload: z.boolean().default(liveConfigDefault.lazy_unload),
|
||||||
draggable: z.boolean().default(liveConfigDefault.draggable),
|
draggable: z.boolean().default(liveConfigDefault.draggable),
|
||||||
|
transition_effect: transitionEffectConfigSchema.default(liveConfigDefault.transition_effect),
|
||||||
})
|
})
|
||||||
.default(liveConfigDefault);
|
.default(liveConfigDefault);
|
||||||
export type LiveConfig = z.infer<typeof liveConfigSchema>;
|
export type LiveConfig = z.infer<typeof liveConfigSchema>;
|
||||||
@@ -608,6 +616,7 @@ const viewerConfigDefault = {
|
|||||||
auto_unmute: false,
|
auto_unmute: false,
|
||||||
lazy_load: true,
|
lazy_load: true,
|
||||||
draggable: true,
|
draggable: true,
|
||||||
|
transition_effect: 'slide' as const,
|
||||||
controls: {
|
controls: {
|
||||||
next_previous: {
|
next_previous: {
|
||||||
size: '48px',
|
size: '48px',
|
||||||
@@ -639,6 +648,7 @@ const viewerConfigSchema = z
|
|||||||
auto_unmute: z.boolean().default(viewerConfigDefault.auto_unmute),
|
auto_unmute: z.boolean().default(viewerConfigDefault.auto_unmute),
|
||||||
lazy_load: z.boolean().default(viewerConfigDefault.lazy_load),
|
lazy_load: z.boolean().default(viewerConfigDefault.lazy_load),
|
||||||
draggable: z.boolean().default(viewerConfigDefault.draggable),
|
draggable: z.boolean().default(viewerConfigDefault.draggable),
|
||||||
|
transition_effect: transitionEffectConfigSchema.default(viewerConfigDefault.transition_effect),
|
||||||
controls: z
|
controls: z
|
||||||
.object({
|
.object({
|
||||||
next_previous: viewerNextPreviousControlConfigSchema.default(
|
next_previous: viewerNextPreviousControlConfigSchema.default(
|
||||||
|
|||||||
Reference in New Issue
Block a user