Initial skeleton menu button priority/alignment.
This commit is contained in:
+15
-1
@@ -285,6 +285,7 @@ export class FrigateCard extends LitElement {
|
|||||||
// Use a magic icon value that the menu will use to render the icon as
|
// Use a magic icon value that the menu will use to render the icon as
|
||||||
// it deems appropriate (certain menu configurations change the menu
|
// it deems appropriate (certain menu configurations change the menu
|
||||||
// icon for the 'Frigate' button).
|
// icon for the 'Frigate' button).
|
||||||
|
priority: this._getConfig().menu.buttons.frigate,
|
||||||
icon: FRIGATE_BUTTON_MENU_ICON,
|
icon: FRIGATE_BUTTON_MENU_ICON,
|
||||||
tap_action: FrigateCardMenu.isHidingMenu(this._getConfig().menu)
|
tap_action: FrigateCardMenu.isHidingMenu(this._getConfig().menu)
|
||||||
? (createFrigateCardCustomAction('menu_toggle') as FrigateCardCustomAction)
|
? (createFrigateCardCustomAction('menu_toggle') as FrigateCardCustomAction)
|
||||||
@@ -311,6 +312,7 @@ export class FrigateCard extends LitElement {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'custom:frigate-card-menu-submenu',
|
type: 'custom:frigate-card-menu-submenu',
|
||||||
title: localize('config.menu.buttons.cameras'),
|
title: localize('config.menu.buttons.cameras'),
|
||||||
|
priority: this._getConfig().menu.buttons.cameras,
|
||||||
icon: 'mdi:video-switch',
|
icon: 'mdi:video-switch',
|
||||||
items: menuItems,
|
items: menuItems,
|
||||||
});
|
});
|
||||||
@@ -320,6 +322,7 @@ export class FrigateCard extends LitElement {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'custom:frigate-card-menu-icon',
|
type: 'custom:frigate-card-menu-icon',
|
||||||
title: localize('config.view.views.live'),
|
title: localize('config.view.views.live'),
|
||||||
|
priority: this._getConfig().menu.buttons.live,
|
||||||
icon: 'mdi:cctv',
|
icon: 'mdi:cctv',
|
||||||
style: this._view?.is('live') ? this._getEmphasizedStyle() : {},
|
style: this._view?.is('live') ? this._getEmphasizedStyle() : {},
|
||||||
tap_action: createFrigateCardCustomAction('live') as FrigateCardCustomAction,
|
tap_action: createFrigateCardCustomAction('live') as FrigateCardCustomAction,
|
||||||
@@ -339,6 +342,7 @@ export class FrigateCard extends LitElement {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'custom:frigate-card-menu-icon',
|
type: 'custom:frigate-card-menu-icon',
|
||||||
title: localize('config.view.views.clips'),
|
title: localize('config.view.views.clips'),
|
||||||
|
priority: this._getConfig().menu.buttons.clips,
|
||||||
icon: 'mdi:filmstrip',
|
icon: 'mdi:filmstrip',
|
||||||
style: this._view?.is('clips') ? this._getEmphasizedStyle() : {},
|
style: this._view?.is('clips') ? this._getEmphasizedStyle() : {},
|
||||||
tap_action: createFrigateCardCustomAction('clips') as FrigateCardCustomAction,
|
tap_action: createFrigateCardCustomAction('clips') as FrigateCardCustomAction,
|
||||||
@@ -357,6 +361,7 @@ export class FrigateCard extends LitElement {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'custom:frigate-card-menu-icon',
|
type: 'custom:frigate-card-menu-icon',
|
||||||
title: localize('config.view.views.snapshots'),
|
title: localize('config.view.views.snapshots'),
|
||||||
|
priority: this._getConfig().menu.buttons.snapshots,
|
||||||
icon: 'mdi:camera',
|
icon: 'mdi:camera',
|
||||||
style: this._view?.is('snapshots') ? this._getEmphasizedStyle() : {},
|
style: this._view?.is('snapshots') ? this._getEmphasizedStyle() : {},
|
||||||
tap_action: createFrigateCardCustomAction(
|
tap_action: createFrigateCardCustomAction(
|
||||||
@@ -372,6 +377,7 @@ export class FrigateCard extends LitElement {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'custom:frigate-card-menu-icon',
|
type: 'custom:frigate-card-menu-icon',
|
||||||
title: localize('config.view.views.image'),
|
title: localize('config.view.views.image'),
|
||||||
|
priority: this._getConfig().menu.buttons.image,
|
||||||
icon: 'mdi:image',
|
icon: 'mdi:image',
|
||||||
style: this._view?.is('image') ? this._getEmphasizedStyle() : {},
|
style: this._view?.is('image') ? this._getEmphasizedStyle() : {},
|
||||||
tap_action: createFrigateCardCustomAction('image') as FrigateCardCustomAction,
|
tap_action: createFrigateCardCustomAction('image') as FrigateCardCustomAction,
|
||||||
@@ -382,6 +388,7 @@ export class FrigateCard extends LitElement {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'custom:frigate-card-menu-icon',
|
type: 'custom:frigate-card-menu-icon',
|
||||||
title: localize('config.view.views.timeline'),
|
title: localize('config.view.views.timeline'),
|
||||||
|
priority: this._getConfig().menu.buttons.timeline,
|
||||||
icon: 'mdi:chart-gantt',
|
icon: 'mdi:chart-gantt',
|
||||||
style: this._view?.is('timeline') ? this._getEmphasizedStyle() : {},
|
style: this._view?.is('timeline') ? this._getEmphasizedStyle() : {},
|
||||||
tap_action: createFrigateCardCustomAction('timeline') as FrigateCardCustomAction,
|
tap_action: createFrigateCardCustomAction('timeline') as FrigateCardCustomAction,
|
||||||
@@ -395,6 +402,7 @@ export class FrigateCard extends LitElement {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'custom:frigate-card-menu-icon',
|
type: 'custom:frigate-card-menu-icon',
|
||||||
title: localize('config.menu.buttons.download'),
|
title: localize('config.menu.buttons.download'),
|
||||||
|
priority: this._getConfig().menu.buttons.download,
|
||||||
icon: 'mdi:download',
|
icon: 'mdi:download',
|
||||||
tap_action: createFrigateCardCustomAction('download') as FrigateCardCustomAction,
|
tap_action: createFrigateCardCustomAction('download') as FrigateCardCustomAction,
|
||||||
});
|
});
|
||||||
@@ -404,6 +412,7 @@ export class FrigateCard extends LitElement {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'custom:frigate-card-menu-icon',
|
type: 'custom:frigate-card-menu-icon',
|
||||||
title: localize('config.menu.buttons.frigate_ui'),
|
title: localize('config.menu.buttons.frigate_ui'),
|
||||||
|
priority: this._getConfig().menu.buttons.frigate_ui,
|
||||||
icon: 'mdi:web',
|
icon: 'mdi:web',
|
||||||
tap_action: createFrigateCardCustomAction(
|
tap_action: createFrigateCardCustomAction(
|
||||||
'frigate_ui',
|
'frigate_ui',
|
||||||
@@ -415,6 +424,7 @@ export class FrigateCard extends LitElement {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'custom:frigate-card-menu-icon',
|
type: 'custom:frigate-card-menu-icon',
|
||||||
title: localize('config.menu.buttons.fullscreen'),
|
title: localize('config.menu.buttons.fullscreen'),
|
||||||
|
priority: this._getConfig().menu.buttons.fullscreen,
|
||||||
icon: screenfull.isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen',
|
icon: screenfull.isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen',
|
||||||
tap_action: createFrigateCardCustomAction(
|
tap_action: createFrigateCardCustomAction(
|
||||||
'fullscreen',
|
'fullscreen',
|
||||||
@@ -489,7 +499,11 @@ export class FrigateCard extends LitElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (this._getConfig().cameras && Array.isArray(this._getConfig().cameras)) {
|
if (this._getConfig().cameras && Array.isArray(this._getConfig().cameras)) {
|
||||||
await Promise.all(this._getConfig().cameras.map(addCameraConfig.bind(this)));
|
// Cameras are loaded sequentially rather than in parallel to preserve the
|
||||||
|
// order of the input camera array.
|
||||||
|
for (const camera of this._getConfig().cameras) {
|
||||||
|
await addCameraConfig(camera);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cameras.size) {
|
if (!cameras.size) {
|
||||||
|
|||||||
+87
-4
@@ -1,4 +1,11 @@
|
|||||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
import {
|
||||||
|
CSSResultGroup,
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
html,
|
||||||
|
unsafeCSS,
|
||||||
|
PropertyValues,
|
||||||
|
} from 'lit';
|
||||||
import { HASSDomEvent, HomeAssistant } from 'custom-card-helpers';
|
import { HASSDomEvent, HomeAssistant } from 'custom-card-helpers';
|
||||||
import { customElement, property, state } from 'lit/decorators.js';
|
import { customElement, property, state } from 'lit/decorators.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
@@ -14,6 +21,7 @@ import type {
|
|||||||
ActionType,
|
ActionType,
|
||||||
MenuButton,
|
MenuButton,
|
||||||
MenuConfig,
|
MenuConfig,
|
||||||
|
MenuItem,
|
||||||
StateParameters,
|
StateParameters,
|
||||||
} from '../types.js';
|
} from '../types.js';
|
||||||
import {
|
import {
|
||||||
@@ -198,6 +206,61 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure menu buttons are sorted before the render.
|
||||||
|
* @param changedProps The changed properties
|
||||||
|
*/
|
||||||
|
protected willUpdate(changedProps: PropertyValues): void {
|
||||||
|
const style = this._menuConfig?.style;
|
||||||
|
const sortButtons = (a: MenuItem, b: MenuItem): number => {
|
||||||
|
// If the menu is hidden, the Frigate button must come first.
|
||||||
|
if (style === 'hidden') {
|
||||||
|
if (a.icon === FRIGATE_BUTTON_MENU_ICON) {
|
||||||
|
return -1;
|
||||||
|
} else if (b.icon === FRIGATE_BUTTON_MENU_ICON) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO below might be removable?
|
||||||
|
|
||||||
|
// If the alignments are different, sort 'opposite' alignments later.
|
||||||
|
if (a.alignment !== b.alignment) {
|
||||||
|
if (
|
||||||
|
(a.alignment === undefined || a.alignment === 'near') &&
|
||||||
|
b.alignment === 'far'
|
||||||
|
) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(b.alignment === undefined || b.alignment === 'near') &&
|
||||||
|
a.alignment === 'far'
|
||||||
|
) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise sort by priority.
|
||||||
|
if (
|
||||||
|
a.priority === undefined ||
|
||||||
|
(b.priority !== undefined && b.priority > a.priority)
|
||||||
|
) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
b.priority === undefined ||
|
||||||
|
(a.priority !== undefined && b.priority < a.priority)
|
||||||
|
) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (changedProps.has('_menuConfig') || changedProps.has('buttons')) {
|
||||||
|
this.buttons.sort(sortButtons);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a button.
|
* Render a button.
|
||||||
* @param button The button configuration to render.
|
* @param button The button configuration to render.
|
||||||
@@ -274,11 +337,31 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the hidden menu isn't expanded, only show the Frigate button.
|
// If the hidden menu isn't expanded, only show the Frigate button.
|
||||||
const buttons =
|
const nearButtons =
|
||||||
style !== 'hidden' || this.expanded
|
style !== 'hidden' || this.expanded
|
||||||
? this.buttons
|
? this.buttons.filter(
|
||||||
|
(button) => !button.alignment || button.alignment === 'near',
|
||||||
|
)
|
||||||
: this.buttons.filter((button) => button.icon === FRIGATE_BUTTON_MENU_ICON);
|
: this.buttons.filter((button) => button.icon === FRIGATE_BUTTON_MENU_ICON);
|
||||||
return html` ${buttons.map((button) => this._renderButton(button))} `;
|
|
||||||
|
const farButtons =
|
||||||
|
style !== 'hidden' || this.expanded
|
||||||
|
? this.buttons.filter((button) => button.alignment === 'far')
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const nearStyle = {
|
||||||
|
flex: String(nearButtons.length),
|
||||||
|
};
|
||||||
|
const farStyle = {
|
||||||
|
flex: String(farButtons.length),
|
||||||
|
};
|
||||||
|
|
||||||
|
return html` <div class="near" style="${styleMap(nearStyle)}">
|
||||||
|
${nearButtons.map((button) => this._renderButton(button))}
|
||||||
|
</div>
|
||||||
|
<div class="far" style="${styleMap(farStyle)}">
|
||||||
|
${farButtons.map((button) => this._renderButton(button))}
|
||||||
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,6 +18,15 @@ import {
|
|||||||
CONF_LIVE_PRELOAD,
|
CONF_LIVE_PRELOAD,
|
||||||
CONF_LIVE_WEBRTC_CARD,
|
CONF_LIVE_WEBRTC_CARD,
|
||||||
CONF_MENU,
|
CONF_MENU,
|
||||||
|
CONF_MENU_BUTTONS_FRIGATE,
|
||||||
|
CONF_MENU_BUTTONS_CAMERAS,
|
||||||
|
CONF_MENU_BUTTONS_LIVE,
|
||||||
|
CONF_MENU_BUTTONS_CLIPS,
|
||||||
|
CONF_MENU_BUTTONS_SNAPSHOTS,
|
||||||
|
CONF_MENU_BUTTONS_IMAGE,
|
||||||
|
CONF_MENU_BUTTONS_DOWNLOAD,
|
||||||
|
CONF_MENU_BUTTONS_FRIGATE_UI,
|
||||||
|
CONF_MENU_BUTTONS_FULLSCREEN,
|
||||||
CONF_MENU_BUTTON_SIZE,
|
CONF_MENU_BUTTON_SIZE,
|
||||||
CONF_MENU_POSITION,
|
CONF_MENU_POSITION,
|
||||||
CONF_MENU_STYLE,
|
CONF_MENU_STYLE,
|
||||||
@@ -28,6 +37,7 @@ import {
|
|||||||
} from './const';
|
} from './const';
|
||||||
import {
|
import {
|
||||||
BUTTON_SIZE_MIN,
|
BUTTON_SIZE_MIN,
|
||||||
|
FRIGATE_MENU_PRIORITY_DEFAULT,
|
||||||
RawFrigateCardConfig,
|
RawFrigateCardConfig,
|
||||||
RawFrigateCardConfigArray,
|
RawFrigateCardConfigArray,
|
||||||
THUMBNAIL_WIDTH_MAX,
|
THUMBNAIL_WIDTH_MAX,
|
||||||
@@ -476,6 +486,21 @@ const upgradeMenuConditionToMenuOverride = (): ((
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform a menu button from a boolean to a priority.
|
||||||
|
* @param value The boolean true/false for show/hide the switch.
|
||||||
|
* @returns A priority value.
|
||||||
|
*/
|
||||||
|
const menuButtonToPriority = function (value: unknown): number | null | undefined {
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (typeof value !== 'boolean') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return value ? FRIGATE_MENU_PRIORITY_DEFAULT : 0;
|
||||||
|
};
|
||||||
|
|
||||||
const UPGRADES = [
|
const UPGRADES = [
|
||||||
// v1.2.1 -> v2.0.0
|
// v1.2.1 -> v2.0.0
|
||||||
upgradeMoveTo('frigate_url', 'frigate.url'),
|
upgradeMoveTo('frigate_url', 'frigate.url'),
|
||||||
@@ -539,4 +564,14 @@ const UPGRADES = [
|
|||||||
),
|
),
|
||||||
upgradeWithOverrides('event_gallery.min_columns', deleteProperty),
|
upgradeWithOverrides('event_gallery.min_columns', deleteProperty),
|
||||||
upgradeMenuModeToStyleAndPosition(),
|
upgradeMenuModeToStyleAndPosition(),
|
||||||
|
|
||||||
|
upgradeWithOverrides(CONF_MENU_BUTTONS_FRIGATE, menuButtonToPriority),
|
||||||
|
upgradeWithOverrides(CONF_MENU_BUTTONS_CAMERAS, menuButtonToPriority),
|
||||||
|
upgradeWithOverrides(CONF_MENU_BUTTONS_LIVE, menuButtonToPriority),
|
||||||
|
upgradeWithOverrides(CONF_MENU_BUTTONS_CLIPS, menuButtonToPriority),
|
||||||
|
upgradeWithOverrides(CONF_MENU_BUTTONS_SNAPSHOTS, menuButtonToPriority),
|
||||||
|
upgradeWithOverrides(CONF_MENU_BUTTONS_IMAGE, menuButtonToPriority),
|
||||||
|
upgradeWithOverrides(CONF_MENU_BUTTONS_DOWNLOAD, menuButtonToPriority),
|
||||||
|
upgradeWithOverrides(CONF_MENU_BUTTONS_FRIGATE_UI, menuButtonToPriority),
|
||||||
|
upgradeWithOverrides(CONF_MENU_BUTTONS_FULLSCREEN, menuButtonToPriority),
|
||||||
];
|
];
|
||||||
|
|||||||
+25
-20
@@ -17,7 +17,8 @@ export const CONF_CAMERAS_ARRAY_TITLE = `${CONF_CAMERAS}.#.title` as const;
|
|||||||
export const CONF_CAMERAS_ARRAY_ICON = `${CONF_CAMERAS}.#.icon` as const;
|
export const CONF_CAMERAS_ARRAY_ICON = `${CONF_CAMERAS}.#.icon` as const;
|
||||||
export const CONF_CAMERAS_ARRAY_WEBRTC_CARD_ENTITY =
|
export const CONF_CAMERAS_ARRAY_WEBRTC_CARD_ENTITY =
|
||||||
`${CONF_CAMERAS}.#.webrtc_card.entity` as const;
|
`${CONF_CAMERAS}.#.webrtc_card.entity` as const;
|
||||||
export const CONF_CAMERAS_ARRAY_WEBRTC_CARD_URL = `${CONF_CAMERAS}.#.webrtc_card.url` as const;
|
export const CONF_CAMERAS_ARRAY_WEBRTC_CARD_URL =
|
||||||
|
`${CONF_CAMERAS}.#.webrtc_card.url` as const;
|
||||||
export const CONF_CAMERAS_ARRAY_LIVE_PROVIDER =
|
export const CONF_CAMERAS_ARRAY_LIVE_PROVIDER =
|
||||||
`${CONF_CAMERAS}.#.live_provider` as const;
|
`${CONF_CAMERAS}.#.live_provider` as const;
|
||||||
export const CONF_CAMERAS_ARRAY_DEPENDENT_CAMERAS =
|
export const CONF_CAMERAS_ARRAY_DEPENDENT_CAMERAS =
|
||||||
@@ -35,9 +36,9 @@ export const CONF_VIEW_UPDATE_SECONDS = `${CONF_VIEW}.update_seconds` as const;
|
|||||||
|
|
||||||
export const CONF_EVENT_GALLERY = 'event_gallery' as const;
|
export const CONF_EVENT_GALLERY = 'event_gallery' as const;
|
||||||
export const CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS =
|
export const CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS =
|
||||||
`${CONF_EVENT_GALLERY}.controls.thumbnails.show_details` as const;
|
`${CONF_EVENT_GALLERY}.controls.thumbnails.show_details` as const;
|
||||||
export const CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_CONTROLS =
|
export const CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_CONTROLS =
|
||||||
`${CONF_EVENT_GALLERY}.controls.thumbnails.show_controls` as const;
|
`${CONF_EVENT_GALLERY}.controls.thumbnails.show_controls` as const;
|
||||||
export const CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SIZE =
|
export const CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SIZE =
|
||||||
`${CONF_EVENT_GALLERY}.controls.thumbnails.size` as const;
|
`${CONF_EVENT_GALLERY}.controls.thumbnails.size` as const;
|
||||||
|
|
||||||
@@ -55,9 +56,9 @@ export const CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_SIZE =
|
|||||||
export const CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_MODE =
|
export const CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_MODE =
|
||||||
`${CONF_EVENT_VIEWER}.controls.thumbnails.mode` as const;
|
`${CONF_EVENT_VIEWER}.controls.thumbnails.mode` as const;
|
||||||
export const CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_SHOW_DETAILS =
|
export const CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_SHOW_DETAILS =
|
||||||
`${CONF_EVENT_VIEWER}.controls.thumbnails.show_details` as const;
|
`${CONF_EVENT_VIEWER}.controls.thumbnails.show_details` as const;
|
||||||
export const CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_SHOW_CONTROLS =
|
export const CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_SHOW_CONTROLS =
|
||||||
`${CONF_EVENT_VIEWER}.controls.thumbnails.show_controls` as const;
|
`${CONF_EVENT_VIEWER}.controls.thumbnails.show_controls` as const;
|
||||||
export const CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_SIZE =
|
export const CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_SIZE =
|
||||||
`${CONF_EVENT_VIEWER}.controls.thumbnails.size` as const;
|
`${CONF_EVENT_VIEWER}.controls.thumbnails.size` as const;
|
||||||
export const CONF_EVENT_VIEWER_CONTROLS_TITLE_MODE =
|
export const CONF_EVENT_VIEWER_CONTROLS_TITLE_MODE =
|
||||||
@@ -78,9 +79,9 @@ export const CONF_LIVE_CONTROLS_THUMBNAILS_MODE =
|
|||||||
export const CONF_LIVE_CONTROLS_THUMBNAILS_SIZE =
|
export const CONF_LIVE_CONTROLS_THUMBNAILS_SIZE =
|
||||||
`${CONF_LIVE}.controls.thumbnails.size` as const;
|
`${CONF_LIVE}.controls.thumbnails.size` as const;
|
||||||
export const CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DETAILS =
|
export const CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DETAILS =
|
||||||
`${CONF_LIVE}.controls.thumbnails.show_details` as const;
|
`${CONF_LIVE}.controls.thumbnails.show_details` as const;
|
||||||
export const CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_CONTROLS =
|
export const CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_CONTROLS =
|
||||||
`${CONF_LIVE}.controls.thumbnails.show_controls` as const;
|
`${CONF_LIVE}.controls.thumbnails.show_controls` as const;
|
||||||
export const CONF_LIVE_CONTROLS_TITLE_MODE = `${CONF_LIVE}.controls.title.mode` as const;
|
export const CONF_LIVE_CONTROLS_TITLE_MODE = `${CONF_LIVE}.controls.title.mode` as const;
|
||||||
export const CONF_LIVE_CONTROLS_TITLE_DURATION_SECONDS =
|
export const CONF_LIVE_CONTROLS_TITLE_DURATION_SECONDS =
|
||||||
`${CONF_LIVE}.controls.title.duration_seconds` as const;
|
`${CONF_LIVE}.controls.title.duration_seconds` as const;
|
||||||
@@ -99,29 +100,33 @@ export const CONF_IMAGE_URL = `${CONF_IMAGE}.url` as const;
|
|||||||
|
|
||||||
export const CONF_TIMELINE = 'timeline' as const;
|
export const CONF_TIMELINE = 'timeline' as const;
|
||||||
export const CONF_TIMELINE_WINDOW_SECONDS = `${CONF_TIMELINE}.window_seconds` as const;
|
export const CONF_TIMELINE_WINDOW_SECONDS = `${CONF_TIMELINE}.window_seconds` as const;
|
||||||
export const CONF_TIMELINE_CLUSTERING_THRESHOLD = `${CONF_TIMELINE}.clustering_threshold` as const;
|
export const CONF_TIMELINE_CLUSTERING_THRESHOLD =
|
||||||
|
`${CONF_TIMELINE}.clustering_threshold` as const;
|
||||||
export const CONF_TIMELINE_MEDIA = `${CONF_TIMELINE}.media` as const;
|
export const CONF_TIMELINE_MEDIA = `${CONF_TIMELINE}.media` as const;
|
||||||
export const CONF_TIMELINE_CONTROLS_THUMBNAILS_MODE = `${CONF_TIMELINE}.controls.thumbnails.mode` as const;
|
export const CONF_TIMELINE_CONTROLS_THUMBNAILS_MODE =
|
||||||
export const CONF_TIMELINE_CONTROLS_THUMBNAILS_SIZE = `${CONF_TIMELINE}.controls.thumbnails.size` as const;
|
`${CONF_TIMELINE}.controls.thumbnails.mode` as const;
|
||||||
export const CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_DETAILS = `${CONF_TIMELINE}.controls.thumbnails.show_details` as const;
|
export const CONF_TIMELINE_CONTROLS_THUMBNAILS_SIZE =
|
||||||
|
`${CONF_TIMELINE}.controls.thumbnails.size` as const;
|
||||||
|
export const CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_DETAILS =
|
||||||
|
`${CONF_TIMELINE}.controls.thumbnails.show_details` as const;
|
||||||
export const CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_CONTROLS =
|
export const CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_CONTROLS =
|
||||||
`${CONF_TIMELINE}.controls.thumbnails.show_controls` as const;
|
`${CONF_TIMELINE}.controls.thumbnails.show_controls` as const;
|
||||||
|
|
||||||
export const CONF_MENU = 'menu' as const;
|
export const CONF_MENU = 'menu' as const;
|
||||||
export const CONF_MENU_ALIGNMENT = `${CONF_MENU}.alignment` as const;
|
export const CONF_MENU_ALIGNMENT = `${CONF_MENU}.alignment` as const;
|
||||||
export const CONF_MENU_POSITION = `${CONF_MENU}.position` as const;
|
export const CONF_MENU_POSITION = `${CONF_MENU}.position` as const;
|
||||||
export const CONF_MENU_STYLE = `${CONF_MENU}.style` as const;
|
export const CONF_MENU_STYLE = `${CONF_MENU}.style` as const;
|
||||||
|
export const CONF_MENU_BUTTON_SIZE = `${CONF_MENU}.button_size` as const;
|
||||||
|
export const CONF_MENU_BUTTONS_CAMERAS = `${CONF_MENU}.buttons.cameras` as const;
|
||||||
|
export const CONF_MENU_BUTTONS_CLIPS = `${CONF_MENU}.buttons.clips` as const;
|
||||||
|
export const CONF_MENU_BUTTONS_DOWNLOAD = `${CONF_MENU}.buttons.download` as const;
|
||||||
export const CONF_MENU_BUTTONS_FRIGATE = `${CONF_MENU}.buttons.frigate` as const;
|
export const CONF_MENU_BUTTONS_FRIGATE = `${CONF_MENU}.buttons.frigate` as const;
|
||||||
export const CONF_MENU_BUTTONS_FRIGATE_UI = `${CONF_MENU}.buttons.frigate_ui` as const;
|
export const CONF_MENU_BUTTONS_FRIGATE_UI = `${CONF_MENU}.buttons.frigate_ui` as const;
|
||||||
export const CONF_MENU_BUTTONS_FRIGATE_FULLSCREEN =
|
export const CONF_MENU_BUTTONS_FULLSCREEN = `${CONF_MENU}.buttons.fullscreen` as const;
|
||||||
`${CONF_MENU}.buttons.fullscreen` as const;
|
|
||||||
export const CONF_MENU_BUTTONS_FRIGATE_DOWNLOAD =
|
|
||||||
`${CONF_MENU}.buttons.download` as const;
|
|
||||||
export const CONF_MENU_BUTTONS_LIVE = `${CONF_MENU}.buttons.live` as const;
|
|
||||||
export const CONF_MENU_BUTTONS_CLIPS = `${CONF_MENU}.buttons.clips` as const;
|
|
||||||
export const CONF_MENU_BUTTONS_SNAPSHOTS = `${CONF_MENU}.buttons.snapshots` as const;
|
|
||||||
export const CONF_MENU_BUTTONS_IMAGE = `${CONF_MENU}.buttons.image` as const;
|
export const CONF_MENU_BUTTONS_IMAGE = `${CONF_MENU}.buttons.image` as const;
|
||||||
export const CONF_MENU_BUTTON_SIZE = `${CONF_MENU}.button_size` as const;
|
export const CONF_MENU_BUTTONS_LIVE = `${CONF_MENU}.buttons.live` as const;
|
||||||
|
|
||||||
|
export const CONF_MENU_BUTTONS_SNAPSHOTS = `${CONF_MENU}.buttons.snapshots` as const;
|
||||||
|
|
||||||
export const CONF_DIMENSIONS = 'dimensions' as const;
|
export const CONF_DIMENSIONS = 'dimensions' as const;
|
||||||
export const CONF_DIMENSIONS_ASPECT_RATIO = `${CONF_DIMENSIONS}.aspect_ratio` as const;
|
export const CONF_DIMENSIONS_ASPECT_RATIO = `${CONF_DIMENSIONS}.aspect_ratio` as const;
|
||||||
|
|||||||
+29
-28
@@ -6,6 +6,7 @@ import { HomeAssistant, LovelaceCardEditor, fireEvent } from 'custom-card-helper
|
|||||||
import { localize } from './localize/localize.js';
|
import { localize } from './localize/localize.js';
|
||||||
import {
|
import {
|
||||||
BUTTON_SIZE_MIN,
|
BUTTON_SIZE_MIN,
|
||||||
|
FRIGATE_MENU_PRIORITY_MAX,
|
||||||
RawFrigateCardConfig,
|
RawFrigateCardConfig,
|
||||||
RawFrigateCardConfigArray,
|
RawFrigateCardConfigArray,
|
||||||
THUMBNAIL_WIDTH_MAX,
|
THUMBNAIL_WIDTH_MAX,
|
||||||
@@ -67,9 +68,9 @@ import {
|
|||||||
CONF_MENU_ALIGNMENT,
|
CONF_MENU_ALIGNMENT,
|
||||||
CONF_MENU_BUTTONS_CLIPS,
|
CONF_MENU_BUTTONS_CLIPS,
|
||||||
CONF_MENU_BUTTONS_FRIGATE,
|
CONF_MENU_BUTTONS_FRIGATE,
|
||||||
CONF_MENU_BUTTONS_FRIGATE_DOWNLOAD,
|
CONF_MENU_BUTTONS_DOWNLOAD,
|
||||||
CONF_MENU_BUTTONS_FRIGATE_FULLSCREEN,
|
|
||||||
CONF_MENU_BUTTONS_FRIGATE_UI,
|
CONF_MENU_BUTTONS_FRIGATE_UI,
|
||||||
|
CONF_MENU_BUTTONS_FULLSCREEN,
|
||||||
CONF_MENU_BUTTONS_IMAGE,
|
CONF_MENU_BUTTONS_IMAGE,
|
||||||
CONF_MENU_BUTTONS_LIVE,
|
CONF_MENU_BUTTONS_LIVE,
|
||||||
CONF_MENU_BUTTONS_SNAPSHOTS,
|
CONF_MENU_BUTTONS_SNAPSHOTS,
|
||||||
@@ -886,45 +887,45 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
${this._renderOptionSelector(CONF_MENU_POSITION, this._menuPositions)}
|
${this._renderOptionSelector(CONF_MENU_POSITION, this._menuPositions)}
|
||||||
${this._renderOptionSelector(CONF_MENU_ALIGNMENT, this._menuAlignments)}
|
${this._renderOptionSelector(CONF_MENU_ALIGNMENT, this._menuAlignments)}
|
||||||
${this._renderNumberInput(CONF_MENU_BUTTON_SIZE, BUTTON_SIZE_MIN)}
|
${this._renderNumberInput(CONF_MENU_BUTTON_SIZE, BUTTON_SIZE_MIN)}
|
||||||
${this._renderSwitch(
|
${this._renderNumberInput(
|
||||||
CONF_MENU_BUTTONS_FRIGATE,
|
CONF_MENU_BUTTONS_FRIGATE,
|
||||||
defaults.menu.buttons.frigate,
|
0,
|
||||||
getShowButtonLabel(CONF_MENU_BUTTONS_FRIGATE),
|
FRIGATE_MENU_PRIORITY_MAX,
|
||||||
)}
|
)}
|
||||||
${this._renderSwitch(
|
${this._renderNumberInput(
|
||||||
CONF_MENU_BUTTONS_LIVE,
|
CONF_MENU_BUTTONS_LIVE,
|
||||||
defaults.menu.buttons.live,
|
0,
|
||||||
getShowButtonLabel('view.views.live'),
|
FRIGATE_MENU_PRIORITY_MAX,
|
||||||
)}
|
)}
|
||||||
${this._renderSwitch(
|
${this._renderNumberInput(
|
||||||
CONF_MENU_BUTTONS_CLIPS,
|
CONF_MENU_BUTTONS_CLIPS,
|
||||||
defaults.menu.buttons.clips,
|
0,
|
||||||
getShowButtonLabel('view.views.clips'),
|
FRIGATE_MENU_PRIORITY_MAX,
|
||||||
)}
|
)}
|
||||||
${this._renderSwitch(
|
${this._renderNumberInput(
|
||||||
CONF_MENU_BUTTONS_SNAPSHOTS,
|
CONF_MENU_BUTTONS_SNAPSHOTS,
|
||||||
defaults.menu.buttons.snapshots,
|
0,
|
||||||
getShowButtonLabel('view.views.snapshots'),
|
FRIGATE_MENU_PRIORITY_MAX,
|
||||||
)}
|
)}
|
||||||
${this._renderSwitch(
|
${this._renderNumberInput(
|
||||||
CONF_MENU_BUTTONS_IMAGE,
|
CONF_MENU_BUTTONS_IMAGE,
|
||||||
defaults.menu.buttons.image,
|
0,
|
||||||
getShowButtonLabel('view.views.image'),
|
FRIGATE_MENU_PRIORITY_MAX,
|
||||||
)}
|
)}
|
||||||
${this._renderSwitch(
|
${this._renderNumberInput(
|
||||||
CONF_MENU_BUTTONS_FRIGATE_DOWNLOAD,
|
CONF_MENU_BUTTONS_DOWNLOAD,
|
||||||
defaults.menu.buttons.download,
|
0,
|
||||||
getShowButtonLabel(CONF_MENU_BUTTONS_FRIGATE_DOWNLOAD),
|
FRIGATE_MENU_PRIORITY_MAX,
|
||||||
)}
|
)}
|
||||||
${this._renderSwitch(
|
${this._renderNumberInput(
|
||||||
CONF_MENU_BUTTONS_FRIGATE_UI,
|
CONF_MENU_BUTTONS_FRIGATE_UI,
|
||||||
defaults.menu.buttons.frigate_ui,
|
0,
|
||||||
getShowButtonLabel(CONF_MENU_BUTTONS_FRIGATE_UI),
|
FRIGATE_MENU_PRIORITY_MAX,
|
||||||
)}
|
)}
|
||||||
${this._renderSwitch(
|
${this._renderNumberInput(
|
||||||
CONF_MENU_BUTTONS_FRIGATE_FULLSCREEN,
|
CONF_MENU_BUTTONS_FULLSCREEN,
|
||||||
defaults.menu.buttons.fullscreen,
|
0,
|
||||||
getShowButtonLabel(CONF_MENU_BUTTONS_FRIGATE_FULLSCREEN),
|
FRIGATE_MENU_PRIORITY_MAX,
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
|||||||
+82
-32
@@ -11,44 +11,77 @@
|
|||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**************************
|
||||||
|
* Aligned divs: near & far
|
||||||
|
**************************/
|
||||||
|
|
||||||
|
div.near,
|
||||||
|
div.far {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
// Allow the divs to be resized.
|
||||||
|
min-width: 0px;
|
||||||
|
min-height: 0px;
|
||||||
|
}
|
||||||
|
div.near {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
div.far {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************
|
||||||
|
* Outside menu style
|
||||||
|
********************/
|
||||||
|
|
||||||
:host([data-style='outside']) {
|
:host([data-style='outside']) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: var(--secondary-background-color);
|
background: var(--secondary-background-color);
|
||||||
}
|
}
|
||||||
:host([data-mode='outside'][data-position='top']) {
|
:host([data-style='outside'][data-position='top']) {
|
||||||
border-top-left-radius: var(--ha-card-border-radius, 4px);
|
border-top-left-radius: var(--ha-card-border-radius, 4px);
|
||||||
border-top-right-radius: var(--ha-card-border-radius, 4px);
|
border-top-right-radius: var(--ha-card-border-radius, 4px);
|
||||||
}
|
}
|
||||||
:host([data-mode='outside'][data-position='below']) {
|
:host([data-style='outside']:not([data-position='top'])) {
|
||||||
border-bottom-left-radius: var(--ha-card-border-radius, 4px);
|
border-bottom-left-radius: var(--ha-card-border-radius, 4px);
|
||||||
border-bottom-right-radius: var(--ha-card-border-radius, 4px);
|
border-bottom-right-radius: var(--ha-card-border-radius, 4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([data-position='top']),
|
/**************************************
|
||||||
:host([data-position='left'][data-alignment='top']),
|
* Positioning for absolute menu styles
|
||||||
:host([data-position='right'][data-alignment='top']) {
|
**************************************/
|
||||||
|
|
||||||
|
:host(:not([data-style='outside'])[data-position='top']),
|
||||||
|
:host(:not([data-style='outside'])[data-position='left'][data-alignment='top']),
|
||||||
|
:host(:not([data-style='outside'])[data-position='right'][data-alignment='top']) {
|
||||||
top: 0px;
|
top: 0px;
|
||||||
}
|
}
|
||||||
:host([data-position='bottom']),
|
:host(:not([data-style='outside'])[data-position='bottom']),
|
||||||
:host([data-position='left'][data-alignment='bottom']),
|
:host(:not([data-style='outside'])[data-position='left'][data-alignment='bottom']),
|
||||||
:host([data-position='right'][data-alignment='bottom']) {
|
:host(:not([data-style='outside'])[data-position='right'][data-alignment='bottom']) {
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
}
|
}
|
||||||
:host([data-position='left']),
|
:host(:not([data-style='outside'])[data-position='left']),
|
||||||
:host([data-position='top'][data-alignment='left']),
|
:host(:not([data-style='outside'])[data-position='top'][data-alignment='left']),
|
||||||
:host([data-position='bottom'][data-alignment='left']) {
|
:host(:not([data-style='outside'])[data-position='bottom'][data-alignment='left']) {
|
||||||
left: 0px;
|
left: 0px;
|
||||||
}
|
}
|
||||||
:host([data-position='right']),
|
:host(:not([data-style='outside'])[data-position='right']),
|
||||||
:host([data-position='top'][data-alignment='right']),
|
:host(:not([data-style='outside'])[data-position='top'][data-alignment='right']),
|
||||||
:host([data-position='bottom'][data-alignment='right']) {
|
:host(:not([data-style='outside'])[data-position='bottom'][data-alignment='right']) {
|
||||||
right: 0px;
|
right: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([data-position='left']) {
|
/********************************************************
|
||||||
|
* Hack: Ensure host & div expand for column flex layouts
|
||||||
|
********************************************************/
|
||||||
|
|
||||||
|
:host(:not([data-style='outside'])[data-position='left']) {
|
||||||
// Awful hack: Flexbox column wrapping doesn't work properly in most major
|
// Awful hack: Flexbox column wrapping doesn't work properly in most major
|
||||||
// browsers -- the element boundary does not expand to cover the full wrapped
|
// browsers -- the element boundary does not expand to cover the full wrapped
|
||||||
// content as it should. This results in the wrapped 'content' appearing
|
// content as it should. This results in the wrapped 'content' appearing
|
||||||
@@ -61,24 +94,51 @@
|
|||||||
// - https://bugs.chromium.org/p/chromium/issues/detail?id=507397
|
// - https://bugs.chromium.org/p/chromium/issues/detail?id=507397
|
||||||
writing-mode: vertical-lr;
|
writing-mode: vertical-lr;
|
||||||
}
|
}
|
||||||
:host([data-position='right']) {
|
:host(:not([data-style='outside'])[data-position='right']) {
|
||||||
// See "Awful hack" above.
|
// See "Awful hack" above.
|
||||||
writing-mode: vertical-rl;
|
writing-mode: vertical-rl;
|
||||||
}
|
}
|
||||||
|
:host(:not([data-style='outside'])[data-style='overlay'][data-position='left']) div > *,
|
||||||
|
:host(:not([data-style='outside'])[data-style='overlay'][data-position='right']) div > *,
|
||||||
|
:host(:not([data-style='outside'])[data-style='hover'][data-position='left']) div > *,
|
||||||
|
:host(:not([data-style='outside'])[data-style='hover'][data-position='right']) div > *,
|
||||||
|
:host(:not([data-style='outside'])[data-style='hidden'][data-position='left']) div > *,
|
||||||
|
:host(:not([data-style='outside'])[data-style='hidden'][data-position='right']) div > * {
|
||||||
|
// See "Awful hack" above. Note that this "cancelation" of writing mode only
|
||||||
|
// affects from beyond the divs, i.e. the writing mode hack applies to host
|
||||||
|
// and divs both.
|
||||||
|
writing-mode: horizontal-tb;
|
||||||
|
}
|
||||||
|
|
||||||
:host([data-position='left'][data-alignment='bottom']),
|
/**********************
|
||||||
:host([data-position='right'][data-alignment='bottom']),
|
* "Reverse" alignments
|
||||||
:host([data-position='top'][data-alignment='right']),
|
**********************/
|
||||||
:host([data-position='bottom'][data-alignment='right']) {
|
|
||||||
|
:host(:not([data-style='outside'])[data-position='left'][data-alignment='bottom']),
|
||||||
|
:host(:not([data-style='outside'])[data-position='right'][data-alignment='bottom']),
|
||||||
|
:host(:not([data-style='outside'])[data-position='top'][data-alignment='right']),
|
||||||
|
:host(:not([data-style='outside'])[data-position='bottom'][data-alignment='right']),
|
||||||
|
:host(:not([data-style='outside'])[data-position='left'][data-alignment='bottom']) div,
|
||||||
|
:host(:not([data-style='outside'])[data-position='right'][data-alignment='bottom']) div,
|
||||||
|
:host(:not([data-style='outside'])[data-position='top'][data-alignment='right']) div,
|
||||||
|
:host(:not([data-style='outside'])[data-position='bottom'][data-alignment='right']) div {
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([data-position='bottom']) {
|
/****************************
|
||||||
|
* Wrap upwards on the bottom
|
||||||
|
****************************/
|
||||||
|
|
||||||
|
:host(:not([data-style='outside'])[data-position='bottom']) div {
|
||||||
// If the menu has more content that allows, "wrap upwards" to keep the
|
// If the menu has more content that allows, "wrap upwards" to keep the
|
||||||
// Frigate button in the same place.
|
// Frigate button in the same place.
|
||||||
flex-wrap: wrap-reverse;
|
flex-wrap: wrap-reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********************************************
|
||||||
|
* Positioning for absolute based menu styles
|
||||||
|
********************************************/
|
||||||
|
|
||||||
:host([data-style='overlay']),
|
:host([data-style='overlay']),
|
||||||
:host([data-style='hover']),
|
:host([data-style='hover']),
|
||||||
:host([data-style='hidden']) {
|
:host([data-style='hidden']) {
|
||||||
@@ -111,13 +171,3 @@
|
|||||||
overflow: visible;
|
overflow: visible;
|
||||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0));
|
background: linear-gradient(180deg, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([data-style='overlay'][data-position='left']) > *,
|
|
||||||
:host([data-style='overlay'][data-position='right']) > *,
|
|
||||||
:host([data-style='hover'][data-position='left']) > *,
|
|
||||||
:host([data-style='hover'][data-position='right']) > *,
|
|
||||||
:host([data-style='hidden'][data-position='left']) > *,
|
|
||||||
:host([data-style='hidden'][data-position='right']) > * {
|
|
||||||
// See "Awful hack" above.
|
|
||||||
writing-mode: horizontal-tb;
|
|
||||||
}
|
|
||||||
|
|||||||
+83
-39
@@ -53,21 +53,13 @@ const FRIGATE_CARD_VIEWS = [
|
|||||||
|
|
||||||
export type FrigateCardView = typeof FRIGATE_CARD_VIEWS[number];
|
export type FrigateCardView = typeof FRIGATE_CARD_VIEWS[number];
|
||||||
|
|
||||||
const FRIGATE_MENU_STYLES = [
|
const FRIGATE_MENU_STYLES = ['none', 'hidden', 'overlay', 'hover', 'outside'] as const;
|
||||||
'none',
|
const FRIGATE_MENU_POSITIONS = ['left', 'right', 'top', 'bottom'] as const;
|
||||||
'hidden',
|
|
||||||
'overlay',
|
|
||||||
'hover',
|
|
||||||
'outside',
|
|
||||||
] as const;
|
|
||||||
const FRIGATE_MENU_POSITIONS = [
|
|
||||||
'left',
|
|
||||||
'right',
|
|
||||||
'top',
|
|
||||||
'bottom',
|
|
||||||
] as const;
|
|
||||||
const FRIGATE_MENU_ALIGNMENTS = FRIGATE_MENU_POSITIONS;
|
const FRIGATE_MENU_ALIGNMENTS = FRIGATE_MENU_POSITIONS;
|
||||||
|
|
||||||
|
export const FRIGATE_MENU_PRIORITY_DEFAULT = 50;
|
||||||
|
export const FRIGATE_MENU_PRIORITY_MAX = 100;
|
||||||
|
|
||||||
const LIVE_PROVIDERS = ['auto', 'ha', 'frigate-jsmpeg', 'webrtc-card'] as const;
|
const LIVE_PROVIDERS = ['auto', 'ha', 'frigate-jsmpeg', 'webrtc-card'] as const;
|
||||||
export type LiveProvider = typeof LIVE_PROVIDERS[number];
|
export type LiveProvider = typeof LIVE_PROVIDERS[number];
|
||||||
|
|
||||||
@@ -336,14 +328,23 @@ export type CameraConfig = z.infer<typeof cameraConfigSchema>;
|
|||||||
* Custom Element Types.
|
* Custom Element Types.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const menuIconSchema = iconSchema.extend({
|
const menuBaseSchema = z.object({
|
||||||
type: z.literal('custom:frigate-card-menu-icon'),
|
priority: z.number().optional(),
|
||||||
|
alignment: z.enum(["near", "far"]).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const menuIconSchema = iconSchema
|
||||||
|
.extend({
|
||||||
|
type: z.literal('custom:frigate-card-menu-icon'),
|
||||||
|
})
|
||||||
|
.merge(menuBaseSchema);
|
||||||
export type MenuIcon = z.infer<typeof menuIconSchema>;
|
export type MenuIcon = z.infer<typeof menuIconSchema>;
|
||||||
|
|
||||||
export const menuStateIconSchema = stateIconSchema.extend({
|
export const menuStateIconSchema = stateIconSchema
|
||||||
|
.extend({
|
||||||
type: z.literal('custom:frigate-card-menu-state-icon'),
|
type: z.literal('custom:frigate-card-menu-state-icon'),
|
||||||
});
|
})
|
||||||
|
.merge(menuBaseSchema);
|
||||||
export type MenuStateIcon = z.infer<typeof menuStateIconSchema>;
|
export type MenuStateIcon = z.infer<typeof menuStateIconSchema>;
|
||||||
|
|
||||||
const menuSubmenuItemSchema = elementsBaseSchema.extend({
|
const menuSubmenuItemSchema = elementsBaseSchema.extend({
|
||||||
@@ -354,11 +355,14 @@ const menuSubmenuItemSchema = elementsBaseSchema.extend({
|
|||||||
});
|
});
|
||||||
export type MenuSubmenuItem = z.infer<typeof menuSubmenuItemSchema>;
|
export type MenuSubmenuItem = z.infer<typeof menuSubmenuItemSchema>;
|
||||||
|
|
||||||
export const menuSubmenuSchema = iconSchema.extend({
|
export const menuSubmenuSchema = iconSchema
|
||||||
|
.extend({
|
||||||
type: z.literal('custom:frigate-card-menu-submenu'),
|
type: z.literal('custom:frigate-card-menu-submenu'),
|
||||||
items: menuSubmenuItemSchema.array(),
|
items: menuSubmenuItemSchema.array(),
|
||||||
});
|
})
|
||||||
|
.merge(menuBaseSchema);
|
||||||
export type MenuSubmenu = z.infer<typeof menuSubmenuSchema>;
|
export type MenuSubmenu = z.infer<typeof menuSubmenuSchema>;
|
||||||
|
export type MenuItem = MenuIcon | MenuStateIcon | MenuSubmenu;
|
||||||
|
|
||||||
const frigateCardConditionSchema = z.object({
|
const frigateCardConditionSchema = z.object({
|
||||||
view: z.string().array().optional(),
|
view: z.string().array().optional(),
|
||||||
@@ -617,16 +621,16 @@ const menuConfigDefault = {
|
|||||||
position: 'top' as const,
|
position: 'top' as const,
|
||||||
alignment: 'left' as const,
|
alignment: 'left' as const,
|
||||||
buttons: {
|
buttons: {
|
||||||
frigate: true,
|
frigate: FRIGATE_MENU_PRIORITY_DEFAULT,
|
||||||
cameras: true,
|
cameras: FRIGATE_MENU_PRIORITY_DEFAULT,
|
||||||
live: true,
|
live: FRIGATE_MENU_PRIORITY_DEFAULT,
|
||||||
clips: true,
|
clips: FRIGATE_MENU_PRIORITY_DEFAULT,
|
||||||
snapshots: true,
|
snapshots: FRIGATE_MENU_PRIORITY_DEFAULT,
|
||||||
image: false,
|
image: 0,
|
||||||
timeline: true,
|
timeline: FRIGATE_MENU_PRIORITY_DEFAULT,
|
||||||
download: true,
|
download: FRIGATE_MENU_PRIORITY_DEFAULT,
|
||||||
frigate_ui: true,
|
frigate_ui: FRIGATE_MENU_PRIORITY_DEFAULT,
|
||||||
fullscreen: true,
|
fullscreen: FRIGATE_MENU_PRIORITY_DEFAULT,
|
||||||
},
|
},
|
||||||
button_size: 40,
|
button_size: 40,
|
||||||
};
|
};
|
||||||
@@ -638,16 +642,56 @@ const menuConfigSchema = z
|
|||||||
alignment: z.enum(FRIGATE_MENU_ALIGNMENTS).default(menuConfigDefault.alignment),
|
alignment: z.enum(FRIGATE_MENU_ALIGNMENTS).default(menuConfigDefault.alignment),
|
||||||
buttons: z
|
buttons: z
|
||||||
.object({
|
.object({
|
||||||
frigate: z.boolean().default(menuConfigDefault.buttons.frigate),
|
frigate: z
|
||||||
cameras: z.boolean().default(menuConfigDefault.buttons.cameras),
|
.number()
|
||||||
live: z.boolean().default(menuConfigDefault.buttons.live),
|
.min(0)
|
||||||
clips: z.boolean().default(menuConfigDefault.buttons.clips),
|
.max(FRIGATE_MENU_PRIORITY_MAX)
|
||||||
snapshots: z.boolean().default(menuConfigDefault.buttons.snapshots),
|
.default(menuConfigDefault.buttons.frigate),
|
||||||
image: z.boolean().default(menuConfigDefault.buttons.image),
|
cameras: z
|
||||||
timeline: z.boolean().default(menuConfigDefault.buttons.timeline),
|
.number()
|
||||||
download: z.boolean().default(menuConfigDefault.buttons.download),
|
.min(0)
|
||||||
frigate_ui: z.boolean().default(menuConfigDefault.buttons.frigate_ui),
|
.max(FRIGATE_MENU_PRIORITY_MAX)
|
||||||
fullscreen: z.boolean().default(menuConfigDefault.buttons.fullscreen),
|
.default(menuConfigDefault.buttons.cameras),
|
||||||
|
live: z
|
||||||
|
.number()
|
||||||
|
.min(0)
|
||||||
|
.max(FRIGATE_MENU_PRIORITY_MAX)
|
||||||
|
.default(menuConfigDefault.buttons.live),
|
||||||
|
clips: z
|
||||||
|
.number()
|
||||||
|
.min(0)
|
||||||
|
.max(FRIGATE_MENU_PRIORITY_MAX)
|
||||||
|
.default(menuConfigDefault.buttons.clips),
|
||||||
|
snapshots: z
|
||||||
|
.number()
|
||||||
|
.min(0)
|
||||||
|
.max(FRIGATE_MENU_PRIORITY_MAX)
|
||||||
|
.default(menuConfigDefault.buttons.snapshots),
|
||||||
|
image: z
|
||||||
|
.number()
|
||||||
|
.min(0)
|
||||||
|
.max(FRIGATE_MENU_PRIORITY_MAX)
|
||||||
|
.default(menuConfigDefault.buttons.image),
|
||||||
|
timeline: z
|
||||||
|
.number()
|
||||||
|
.min(0)
|
||||||
|
.max(FRIGATE_MENU_PRIORITY_MAX)
|
||||||
|
.default(menuConfigDefault.buttons.timeline),
|
||||||
|
download: z
|
||||||
|
.number()
|
||||||
|
.min(0)
|
||||||
|
.max(FRIGATE_MENU_PRIORITY_MAX)
|
||||||
|
.default(menuConfigDefault.buttons.download),
|
||||||
|
frigate_ui: z
|
||||||
|
.number()
|
||||||
|
.min(0)
|
||||||
|
.max(FRIGATE_MENU_PRIORITY_MAX)
|
||||||
|
.default(menuConfigDefault.buttons.frigate_ui),
|
||||||
|
fullscreen: z
|
||||||
|
.number()
|
||||||
|
.min(0)
|
||||||
|
.max(FRIGATE_MENU_PRIORITY_MAX)
|
||||||
|
.default(menuConfigDefault.buttons.fullscreen),
|
||||||
})
|
})
|
||||||
.default(menuConfigDefault.buttons),
|
.default(menuConfigDefault.buttons),
|
||||||
button_size: z.number().min(BUTTON_SIZE_MIN).default(menuConfigDefault.button_size),
|
button_size: z.number().min(BUTTON_SIZE_MIN).default(menuConfigDefault.button_size),
|
||||||
|
|||||||
Reference in New Issue
Block a user