Merge pull request #524 from dermotduffy/menu-versatility
Improve menu versatility and add menu alignment
This commit is contained in:
@@ -171,7 +171,9 @@ menu:
|
|||||||
|
|
||||||
| Option | Default | Overridable | Description |
|
| Option | Default | Overridable | Description |
|
||||||
| - | - | - | - |
|
| - | - | - | - |
|
||||||
| `mode` | `hidden-top` | :white_check_mark: | The menu mode to show by default. See [menu modes](#menu-modes) below.|
|
| `style` | `hidden` | :white_check_mark: | The menu style to show by default, one of `none`, `hidden`, `hover`, `overlay`, or `outside`. See [menu styles](#menu-styles) below.|
|
||||||
|
| `position` | `top` | :white_check_mark: | Whether to show the menu on the `left`, `right`, `top` or `bottom` side of the card.|
|
||||||
|
| `alignment` | `left` | :white_check_mark: | Whether to align the menu buttons to the `left`, `right`, `top` or `bottom` of the menu. Some selections may have no effect depending on the value of `position` (e.g. it doesn't make sense to `left` align icons on a menu with `position` to the `left`).|
|
||||||
| `button_size` | 40 | :white_check_mark: | The size of the menu buttons in pixels. Must be >= `20`.|
|
| `button_size` | 40 | :white_check_mark: | The size of the menu buttons in pixels. Must be >= `20`.|
|
||||||
| `buttons` | | :white_check_mark: | Whether to show or hide built-in buttons. See below. |
|
| `buttons` | | :white_check_mark: | Whether to show or hide built-in buttons. See below. |
|
||||||
|
|
||||||
@@ -186,7 +188,7 @@ menu:
|
|||||||
|
|
||||||
| Option | Default | Overridable | Description |
|
| Option | Default | Overridable | Description |
|
||||||
| - | - | - | - |
|
| - | - | - | - |
|
||||||
| `frigate` | `true` | :white_check_mark: | Whether to show the `Frigate` menu button: brings the user to the default configured view (`view.default`), or collapses/expands the menu if the `menu.mode` is `hidden-*` . |
|
| `frigate` | `true` | :white_check_mark: | Whether to show the `Frigate` menu button: brings the user to the default configured view (`view.default`), or collapses/expands the menu if the `menu.style` is `hidden` . |
|
||||||
| `cameras` | `true` | :white_check_mark: | Whether to show the camera selection submenu. Will only appear if multiple cameras are configured. |
|
| `cameras` | `true` | :white_check_mark: | Whether to show the camera selection submenu. Will only appear if multiple cameras are configured. |
|
||||||
| `live` | `true` | :white_check_mark: | Whether to show the `live` view menu button: brings the user to the `live` view. See [views](#views) below.|
|
| `live` | `true` | :white_check_mark: | Whether to show the `live` view menu button: brings the user to the `live` view. See [views](#views) below.|
|
||||||
| `clips` | `true` | :white_check_mark: | Whether to show the `clips` view menu button: brings the user to the `clips` view on tap and the most-recent `clip` view on hold. See [views](#views) below. This button will never be shown if the `camera_name` for the selected camera is not auto-detected/specified (e.g. non-Frigate cameras), or if the `camera_name` is `birdseye`.|
|
| `clips` | `true` | :white_check_mark: | Whether to show the `clips` view menu button: brings the user to the `clips` view on tap and the most-recent `clip` view on hold. See [views](#views) below. This button will never be shown if the `camera_name` for the selected camera is not auto-detected/specified (e.g. non-Frigate cameras), or if the `camera_name` is `birdseye`.|
|
||||||
@@ -655,7 +657,7 @@ Parameters for the `custom:frigate-card-conditional` element:
|
|||||||
|`frigate_ui`|Open the Frigate UI at the configured URL.|
|
|`frigate_ui`|Open the Frigate UI at the configured URL.|
|
||||||
|`fullscreen`|Toggle fullscreen.|
|
|`fullscreen`|Toggle fullscreen.|
|
||||||
|`camera_select`|Select a given camera. Takes a single additional `camera` parameter with the [camera ID](#camera-ids) of the camera to select. Respects the value of `view.camera_select` to choose the appropriate view on the new camera.|
|
|`camera_select`|Select a given camera. Takes a single additional `camera` parameter with the [camera ID](#camera-ids) of the camera to select. Respects the value of `view.camera_select` to choose the appropriate view on the new camera.|
|
||||||
|`menu_toggle` | Show/hide the menu (for `hidden-*` mode menus). |
|
|`menu_toggle` | Show/hide the menu (for the `hidden` mode style). |
|
||||||
|
|
||||||
<a name="views"></a>
|
<a name="views"></a>
|
||||||
|
|
||||||
@@ -747,17 +749,16 @@ next/previous controls, thumbnails, etc), but in some cases this is not possible
|
|||||||
(e.g. embedded WebRTC card controls) -- in these cases duplicate actions may
|
(e.g. embedded WebRTC card controls) -- in these cases duplicate actions may
|
||||||
occur with certain configurations (e.g. `tap`).
|
occur with certain configurations (e.g. `tap`).
|
||||||
|
|
||||||
## Menu Modes
|
## Menu Styles
|
||||||
|
|
||||||
This card supports several menu configurations.
|
This card supports several menu styles.
|
||||||
|
|
||||||
| Key | Description | Screenshot |
|
| Key | Description | Screenshot |
|
||||||
| ------------- | --------------------------------------------- | - |
|
| ------------- | --------------------------------------------- | - |
|
||||||
|`hidden-{top,bottom,right,left}` [default: `hidden-top`]| Hide the menu by default, expandable upon clicking the Frigate button. | <img src="https://raw.githubusercontent.com/dermotduffy/frigate-hass-card/main/images/menu-mode-hidden.png" alt="Menu hidden" width="400px"> |
|
|`hidden`| Hide the menu by default, expandable upon clicking the Frigate button. | <img src="https://raw.githubusercontent.com/dermotduffy/frigate-hass-card/main/images/menu-mode-hidden.png" alt="Menu hidden" width="400px"> |
|
||||||
|`overlay-{top,bottom,right,left}`| Overlay the menu over the card contents. The Frigate button shows the default view. | <img src="https://raw.githubusercontent.com/dermotduffy/frigate-hass-card/main/images/menu-mode-overlay.png" alt="Menu overlaid" width="400px"> |
|
|`overlay`| Overlay the menu over the card contents. The Frigate button shows the default view. | <img src="https://raw.githubusercontent.com/dermotduffy/frigate-hass-card/main/images/menu-mode-overlay.png" alt="Menu overlaid" width="400px"> |
|
||||||
|`hover-{top,bottom,right,left}`| Overlay the menu over the card contents when the mouse is over the card / touch on the card, otherwise it is not shown. The Frigate button shows the default view. | <img src="https://raw.githubusercontent.com/dermotduffy/frigate-hass-card/main/images/menu-mode-overlay.png" alt="Menu overlaid" width="400px"> |
|
|`hover`| Overlay the menu over the card contents when the mouse is over the card / touch on the card, otherwise it is not shown. The Frigate button shows the default view. | <img src="https://raw.githubusercontent.com/dermotduffy/frigate-hass-card/main/images/menu-mode-overlay.png" alt="Menu overlaid" width="400px"> |
|
||||||
|`above`| Render the menu above the card. The Frigate button shows the default view. | <img src="https://raw.githubusercontent.com/dermotduffy/frigate-hass-card/main/images/menu-mode-above.png" alt="Menu above" width="400px"> |
|
|`outside`| Render the menu outside the card (i.e. above it if `position` is `top`, or below it if `position` is `bottom`). The Frigate button shows the default view. | <img src="https://raw.githubusercontent.com/dermotduffy/frigate-hass-card/main/images/menu-mode-above.png" alt="Menu above" width="400px"> |
|
||||||
|`below`| Render the menu below the card. The Frigate button shows the default view. | <img src="https://raw.githubusercontent.com/dermotduffy/frigate-hass-card/main/images/menu-mode-below.png" alt="Menu below" width="400px"> |
|
|
||||||
|`none`| No menu is shown. | <img src="https://raw.githubusercontent.com/dermotduffy/frigate-hass-card/main/images/menu-mode-none.png" alt="No Menu" width="400px"> |
|
|`none`| No menu is shown. | <img src="https://raw.githubusercontent.com/dermotduffy/frigate-hass-card/main/images/menu-mode-none.png" alt="No Menu" width="400px"> |
|
||||||
|
|
||||||
<a name="screenshots"></a>
|
<a name="screenshots"></a>
|
||||||
@@ -1189,7 +1190,7 @@ overrides:
|
|||||||
fullscreen: true
|
fullscreen: true
|
||||||
overrides:
|
overrides:
|
||||||
menu:
|
menu:
|
||||||
mode: none
|
style: none
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|||||||
+5
-2
@@ -1203,6 +1203,9 @@ export class FrigateCard extends LitElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const actions = this._getMergedActions();
|
const actions = this._getMergedActions();
|
||||||
|
const renderMenuAbove =
|
||||||
|
this._getConfig().menu.style === 'outside' &&
|
||||||
|
this._getConfig().menu.position === 'top';
|
||||||
|
|
||||||
return html` <ha-card
|
return html` <ha-card
|
||||||
.actionHandler=${actionHandler({
|
.actionHandler=${actionHandler({
|
||||||
@@ -1216,7 +1219,7 @@ export class FrigateCard extends LitElement {
|
|||||||
@frigate-card:media-show=${this._mediaShowHandler}
|
@frigate-card:media-show=${this._mediaShowHandler}
|
||||||
@frigate-card:render=${() => this.requestUpdate()}
|
@frigate-card:render=${() => this.requestUpdate()}
|
||||||
>
|
>
|
||||||
${this._getConfig().menu.mode == 'above' ? this._renderMenu() : ''}
|
${renderMenuAbove ? this._renderMenu() : ''}
|
||||||
<div class="container outer" style="${styleMap(outerStyle)}">
|
<div class="container outer" style="${styleMap(outerStyle)}">
|
||||||
<div class="${classMap(contentClasses)}">
|
<div class="${classMap(contentClasses)}">
|
||||||
${this._cameras === undefined
|
${this._cameras === undefined
|
||||||
@@ -1261,7 +1264,7 @@ export class FrigateCard extends LitElement {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${this._getConfig().menu.mode != 'above' ? this._renderMenu() : ''}
|
${!renderMenuAbove ? this._renderMenu() : ''}
|
||||||
</ha-card>`;
|
</ha-card>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,8 +64,11 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
`${menuConfig.button_size}px`,
|
`${menuConfig.button_size}px`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Store the menu mode as an attribute (used for CSS attribute selectors).
|
// Store the menu style, position and alignment as attributes (used for
|
||||||
this.setAttribute('data-mode', menuConfig.mode);
|
// styling).
|
||||||
|
this.setAttribute('data-style', menuConfig.style);
|
||||||
|
this.setAttribute('data-position', menuConfig.position);
|
||||||
|
this.setAttribute('data-alignment', menuConfig.alignment);
|
||||||
}
|
}
|
||||||
@state()
|
@state()
|
||||||
protected _menuConfig?: MenuConfig;
|
protected _menuConfig?: MenuConfig;
|
||||||
@@ -82,7 +85,7 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
* @returns `true` if the menu is hiding, `false` otherwise.
|
* @returns `true` if the menu is hiding, `false` otherwise.
|
||||||
*/
|
*/
|
||||||
static isHidingMenu(menuConfig: MenuConfig | undefined): boolean {
|
static isHidingMenu(menuConfig: MenuConfig | undefined): boolean {
|
||||||
return menuConfig?.mode.startsWith('hidden-') ?? false;
|
return menuConfig?.style === 'hidden' ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,15 +268,14 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
if (!this._menuConfig) {
|
if (!this._menuConfig) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const mode = this._menuConfig.mode;
|
const style = this._menuConfig.style;
|
||||||
|
if (style === 'none') {
|
||||||
if (mode == 'none') {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 buttons =
|
||||||
!mode.startsWith('hidden-') || this.expanded
|
style !== 'hidden' || this.expanded
|
||||||
? this.buttons
|
? this.buttons
|
||||||
: 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))} `;
|
return html` ${buttons.map((button) => this._renderButton(button))} `;
|
||||||
|
|||||||
+89
-15
@@ -19,7 +19,8 @@ import {
|
|||||||
CONF_LIVE_WEBRTC_CARD,
|
CONF_LIVE_WEBRTC_CARD,
|
||||||
CONF_MENU,
|
CONF_MENU,
|
||||||
CONF_MENU_BUTTON_SIZE,
|
CONF_MENU_BUTTON_SIZE,
|
||||||
CONF_MENU_MODE,
|
CONF_MENU_POSITION,
|
||||||
|
CONF_MENU_STYLE,
|
||||||
CONF_OVERRIDES,
|
CONF_OVERRIDES,
|
||||||
CONF_VIEW_DEFAULT,
|
CONF_VIEW_DEFAULT,
|
||||||
CONF_VIEW_TIMEOUT_SECONDS,
|
CONF_VIEW_TIMEOUT_SECONDS,
|
||||||
@@ -203,7 +204,7 @@ const toPixelsOrDelete = function (value: unknown): number | null | undefined {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const deleteProperty = function (_value: unknown): number | null | undefined {
|
const deleteProperty = function (_value: unknown): number | null | undefined {
|
||||||
return null;
|
return null;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move a property from one location to another.
|
* Move a property from one location to another.
|
||||||
@@ -218,6 +219,7 @@ export const moveConfigValue = (
|
|||||||
oldPath: string,
|
oldPath: string,
|
||||||
newPath: string,
|
newPath: string,
|
||||||
transform?: (valueIn: unknown) => unknown,
|
transform?: (valueIn: unknown) => unknown,
|
||||||
|
keepOriginal?: boolean,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
const inValue = getConfigValue(obj, oldPath);
|
const inValue = getConfigValue(obj, oldPath);
|
||||||
if (inValue === undefined) {
|
if (inValue === undefined) {
|
||||||
@@ -228,11 +230,16 @@ export const moveConfigValue = (
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (outValue === null) {
|
if (outValue === null) {
|
||||||
deleteConfigValue(obj, oldPath);
|
if (!keepOriginal) {
|
||||||
return true;
|
deleteConfigValue(obj, oldPath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if (outValue !== undefined) {
|
if (outValue !== undefined) {
|
||||||
deleteConfigValue(obj, oldPath);
|
if (!keepOriginal) {
|
||||||
|
deleteConfigValue(obj, oldPath);
|
||||||
|
}
|
||||||
setConfigValue(obj, newPath, outValue);
|
setConfigValue(obj, newPath, outValue);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -260,9 +267,10 @@ const upgradeMoveTo = function (
|
|||||||
oldPath: string,
|
oldPath: string,
|
||||||
newPath: string,
|
newPath: string,
|
||||||
transform?: (valueIn: unknown) => unknown,
|
transform?: (valueIn: unknown) => unknown,
|
||||||
|
keepOriginal?: boolean,
|
||||||
): (obj: RawFrigateCardConfig) => boolean {
|
): (obj: RawFrigateCardConfig) => boolean {
|
||||||
return function (obj: RawFrigateCardConfig): boolean {
|
return function (obj: RawFrigateCardConfig): boolean {
|
||||||
return moveConfigValue(obj, oldPath, newPath, transform);
|
return moveConfigValue(obj, oldPath, newPath, transform, keepOriginal);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -278,13 +286,14 @@ const upgradeMoveToWithOverrides = function (
|
|||||||
oldPath: string,
|
oldPath: string,
|
||||||
newPath: string,
|
newPath: string,
|
||||||
transform?: (valueIn: unknown) => unknown,
|
transform?: (valueIn: unknown) => unknown,
|
||||||
|
keepOriginal?: boolean,
|
||||||
): (obj: RawFrigateCardConfig) => boolean {
|
): (obj: RawFrigateCardConfig) => boolean {
|
||||||
return function (obj: RawFrigateCardConfig): boolean {
|
return function (obj: RawFrigateCardConfig): boolean {
|
||||||
let modified = upgradeMoveTo(oldPath, newPath, transform)(obj);
|
let modified = upgradeMoveTo(oldPath, newPath, transform, keepOriginal)(obj);
|
||||||
modified =
|
modified =
|
||||||
upgradeArrayValue(
|
upgradeArrayValue(
|
||||||
CONF_OVERRIDES,
|
CONF_OVERRIDES,
|
||||||
upgradeMoveTo(oldPath, newPath, transform),
|
upgradeMoveTo(oldPath, newPath, transform, keepOriginal),
|
||||||
(obj) => obj.overrides as RawFrigateCardConfig | undefined,
|
(obj) => obj.overrides as RawFrigateCardConfig | undefined,
|
||||||
)(obj) || modified;
|
)(obj) || modified;
|
||||||
return modified;
|
return modified;
|
||||||
@@ -334,8 +343,7 @@ const upgradeArrayValue = function (
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Upgrade from a singular camera model to multiple.
|
* Upgrade from a singular camera model to multiple.
|
||||||
* @param key A string key.
|
* @returns An upgrade function.
|
||||||
* @returns A safe key.
|
|
||||||
*/
|
*/
|
||||||
const upgradeToMultipleCameras = (): ((obj: RawFrigateCardConfig) => boolean) => {
|
const upgradeToMultipleCameras = (): ((obj: RawFrigateCardConfig) => boolean) => {
|
||||||
return function (obj: RawFrigateCardConfig): boolean {
|
return function (obj: RawFrigateCardConfig): boolean {
|
||||||
@@ -366,6 +374,74 @@ const upgradeToMultipleCameras = (): ((obj: RawFrigateCardConfig) => boolean) =>
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrade from a menu-mode to a style & position.
|
||||||
|
* @returns An upgrade function.
|
||||||
|
*/
|
||||||
|
const upgradeMenuModeToStyleAndPosition = (): ((
|
||||||
|
obj: RawFrigateCardConfig,
|
||||||
|
) => boolean) => {
|
||||||
|
return function (obj: RawFrigateCardConfig): boolean {
|
||||||
|
let modified = false;
|
||||||
|
|
||||||
|
// Change the 'start' of the mode into a style.
|
||||||
|
modified =
|
||||||
|
upgradeMoveToWithOverrides(
|
||||||
|
'menu.mode',
|
||||||
|
CONF_MENU_STYLE,
|
||||||
|
(mode: unknown): string | undefined => {
|
||||||
|
if (typeof mode === 'string') {
|
||||||
|
const result = mode.match(/^(hover|hidden|overlay|above|below|none)/);
|
||||||
|
if (result) {
|
||||||
|
switch (result[1]) {
|
||||||
|
case 'hover':
|
||||||
|
case 'hidden':
|
||||||
|
case 'overlay':
|
||||||
|
case 'none':
|
||||||
|
return result[1];
|
||||||
|
case 'above':
|
||||||
|
case 'below':
|
||||||
|
return 'outside';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)(obj) || modified;
|
||||||
|
|
||||||
|
// Change the 'end' of the mode into a position.
|
||||||
|
modified =
|
||||||
|
upgradeMoveToWithOverrides(
|
||||||
|
'menu.mode',
|
||||||
|
CONF_MENU_POSITION,
|
||||||
|
(mode: unknown): string | undefined => {
|
||||||
|
if (typeof mode === 'string') {
|
||||||
|
const result = mode.match(/(above|below|left|right|top|bottom)$/);
|
||||||
|
if (result) {
|
||||||
|
switch (result[1]) {
|
||||||
|
case 'left':
|
||||||
|
case 'right':
|
||||||
|
case 'top':
|
||||||
|
case 'bottom':
|
||||||
|
return result[1];
|
||||||
|
case 'above':
|
||||||
|
return 'top';
|
||||||
|
case 'below':
|
||||||
|
return 'bottom';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)(obj) || modified;
|
||||||
|
|
||||||
|
// Delete the old `menu.mode` .
|
||||||
|
return upgradeWithOverrides('menu.mode', deleteProperty)(obj) || modified;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upgrade from a condition on the menu (to allow rendering) to a menu mode
|
* Upgrade from a condition on the menu (to allow rendering) to a menu mode
|
||||||
* override instead.
|
* override instead.
|
||||||
@@ -415,7 +491,7 @@ const UPGRADES = [
|
|||||||
upgradeMoveTo('autoplay_clip', 'event_viewer.autoplay_clip'),
|
upgradeMoveTo('autoplay_clip', 'event_viewer.autoplay_clip'),
|
||||||
upgradeMoveTo('controls.nextprev', CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE),
|
upgradeMoveTo('controls.nextprev', CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE),
|
||||||
upgradeMoveTo('controls.nextprev_size', CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_SIZE),
|
upgradeMoveTo('controls.nextprev_size', CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_SIZE),
|
||||||
upgradeMoveTo('menu_mode', CONF_MENU_MODE),
|
upgradeMoveTo('menu_mode', 'menu.mode'),
|
||||||
upgradeMoveTo('menu_buttons', 'menu.buttons'),
|
upgradeMoveTo('menu_buttons', 'menu.buttons'),
|
||||||
upgradeMoveTo('menu_button_size', CONF_MENU_BUTTON_SIZE),
|
upgradeMoveTo('menu_button_size', CONF_MENU_BUTTON_SIZE),
|
||||||
upgradeMoveTo('image', 'image.src', isNotObject),
|
upgradeMoveTo('image', 'image.src', isNotObject),
|
||||||
@@ -461,8 +537,6 @@ const UPGRADES = [
|
|||||||
CONF_MENU_BUTTON_SIZE,
|
CONF_MENU_BUTTON_SIZE,
|
||||||
createRangedTransform(toPixelsOrDelete, BUTTON_SIZE_MIN),
|
createRangedTransform(toPixelsOrDelete, BUTTON_SIZE_MIN),
|
||||||
),
|
),
|
||||||
upgradeWithOverrides(
|
upgradeWithOverrides('event_gallery.min_columns', deleteProperty),
|
||||||
'event_gallery.min_columns',
|
upgradeMenuModeToStyleAndPosition(),
|
||||||
deleteProperty
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|||||||
+3
-1
@@ -108,6 +108,9 @@ 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_POSITION = `${CONF_MENU}.position` as const;
|
||||||
|
export const CONF_MENU_STYLE = `${CONF_MENU}.style` 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_FRIGATE_FULLSCREEN =
|
||||||
@@ -119,7 +122,6 @@ 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_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_BUTTON_SIZE = `${CONF_MENU}.button_size` as const;
|
||||||
export const CONF_MENU_MODE = `${CONF_MENU}.mode` 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
-23
@@ -64,6 +64,7 @@ import {
|
|||||||
CONF_LIVE_LAZY_UNLOAD,
|
CONF_LIVE_LAZY_UNLOAD,
|
||||||
CONF_LIVE_PRELOAD,
|
CONF_LIVE_PRELOAD,
|
||||||
CONF_LIVE_TRANSITION_EFFECT,
|
CONF_LIVE_TRANSITION_EFFECT,
|
||||||
|
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_FRIGATE_DOWNLOAD,
|
||||||
@@ -73,7 +74,8 @@ import {
|
|||||||
CONF_MENU_BUTTONS_LIVE,
|
CONF_MENU_BUTTONS_LIVE,
|
||||||
CONF_MENU_BUTTONS_SNAPSHOTS,
|
CONF_MENU_BUTTONS_SNAPSHOTS,
|
||||||
CONF_MENU_BUTTON_SIZE,
|
CONF_MENU_BUTTON_SIZE,
|
||||||
CONF_MENU_MODE,
|
CONF_MENU_POSITION,
|
||||||
|
CONF_MENU_STYLE,
|
||||||
CONF_TIMELINE_CLUSTERING_THRESHOLD,
|
CONF_TIMELINE_CLUSTERING_THRESHOLD,
|
||||||
CONF_TIMELINE_CONTROLS_THUMBNAILS_MODE,
|
CONF_TIMELINE_CONTROLS_THUMBNAILS_MODE,
|
||||||
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_CONTROLS,
|
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_CONTROLS,
|
||||||
@@ -89,11 +91,7 @@ import {
|
|||||||
CONF_VIEW_UPDATE_FORCE,
|
CONF_VIEW_UPDATE_FORCE,
|
||||||
CONF_VIEW_UPDATE_SECONDS,
|
CONF_VIEW_UPDATE_SECONDS,
|
||||||
} from './const.js';
|
} from './const.js';
|
||||||
import {
|
import { arrayMove, getCameraID, getCameraTitle } from './common.js';
|
||||||
arrayMove,
|
|
||||||
getCameraID,
|
|
||||||
getCameraTitle,
|
|
||||||
} from './common.js';
|
|
||||||
import {
|
import {
|
||||||
copyConfig,
|
copyConfig,
|
||||||
deleteConfigValue,
|
deleteConfigValue,
|
||||||
@@ -219,23 +217,29 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
{ value: 'current', label: localize('config.view.views.current') },
|
{ value: 'current', label: localize('config.view.views.current') },
|
||||||
];
|
];
|
||||||
|
|
||||||
protected _menuModes: EditorSelectOption[] = [
|
protected _menuStyles: EditorSelectOption[] = [
|
||||||
{ value: '', label: '' },
|
{ value: '', label: '' },
|
||||||
{ value: 'none', label: localize('config.menu.modes.none') },
|
{ value: 'none', label: localize('config.menu.styles.none') },
|
||||||
{ value: 'hidden-top', label: localize('config.menu.modes.hidden-top') },
|
{ value: 'hidden', label: localize('config.menu.styles.hidden') },
|
||||||
{ value: 'hidden-left', label: localize('config.menu.modes.hidden-left') },
|
{ value: 'overlay', label: localize('config.menu.styles.overlay') },
|
||||||
{ value: 'hidden-bottom', label: localize('config.menu.modes.hidden-bottom') },
|
{ value: 'hover', label: localize('config.menu.styles.hover') },
|
||||||
{ value: 'hidden-right', label: localize('config.menu.modes.hidden-right') },
|
{ value: 'outside', label: localize('config.menu.styles.outside') },
|
||||||
{ value: 'overlay-top', label: localize('config.menu.modes.overlay-top') },
|
];
|
||||||
{ value: 'overlay-left', label: localize('config.menu.modes.overlay-left') },
|
|
||||||
{ value: 'overlay-bottom', label: localize('config.menu.modes.overlay-bottom') },
|
protected _menuPositions: EditorSelectOption[] = [
|
||||||
{ value: 'overlay-right', label: localize('config.menu.modes.overlay-right') },
|
{ value: '', label: '' },
|
||||||
{ value: 'hover-top', label: localize('config.menu.modes.hover-top') },
|
{ value: 'left', label: localize('config.menu.positions.left') },
|
||||||
{ value: 'hover-left', label: localize('config.menu.modes.hover-left') },
|
{ value: 'right', label: localize('config.menu.positions.right') },
|
||||||
{ value: 'hover-bottom', label: localize('config.menu.modes.hover-bottom') },
|
{ value: 'top', label: localize('config.menu.positions.top') },
|
||||||
{ value: 'hover-right', label: localize('config.menu.modes.hover-right') },
|
{ value: 'bottom', label: localize('config.menu.positions.bottom') },
|
||||||
{ value: 'above', label: localize('config.menu.modes.above') },
|
];
|
||||||
{ value: 'below', label: localize('config.menu.modes.below') },
|
|
||||||
|
protected _menuAlignments: EditorSelectOption[] = [
|
||||||
|
{ value: '', label: '' },
|
||||||
|
{ value: 'left', label: localize('config.menu.alignments.left') },
|
||||||
|
{ value: 'right', label: localize('config.menu.alignments.right') },
|
||||||
|
{ value: 'top', label: localize('config.menu.alignments.top') },
|
||||||
|
{ value: 'bottom', label: localize('config.menu.alignments.bottom') },
|
||||||
];
|
];
|
||||||
|
|
||||||
protected _eventViewerNextPreviousControlStyles: EditorSelectOption[] = [
|
protected _eventViewerNextPreviousControlStyles: EditorSelectOption[] = [
|
||||||
@@ -878,7 +882,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
${options.menu.show
|
${options.menu.show
|
||||||
? html`
|
? html`
|
||||||
<div class="values">
|
<div class="values">
|
||||||
${this._renderOptionSelector(CONF_MENU_MODE, this._menuModes)}
|
${this._renderOptionSelector(CONF_MENU_STYLE, this._menuStyles)}
|
||||||
|
${this._renderOptionSelector(CONF_MENU_POSITION, this._menuPositions)}
|
||||||
|
${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._renderSwitch(
|
||||||
CONF_MENU_BUTTONS_FRIGATE,
|
CONF_MENU_BUTTONS_FRIGATE,
|
||||||
|
|||||||
@@ -164,23 +164,27 @@
|
|||||||
"download": "Download event media",
|
"download": "Download event media",
|
||||||
"cameras": "Select camera"
|
"cameras": "Select camera"
|
||||||
},
|
},
|
||||||
"mode": "Menu mode",
|
"style": "Menu style",
|
||||||
"modes": {
|
"styles": {
|
||||||
"none": "No menu",
|
"none": "No menu",
|
||||||
"hidden-top": "Hidden top",
|
"hidden": "Hidden menu",
|
||||||
"hidden-left": "Hidden left",
|
"overlay": "Overlay menu",
|
||||||
"hidden-bottom": "Hidden bottom",
|
"hover": "Hover menu",
|
||||||
"hidden-right": "Hidden right",
|
"outside": "Outside menu"
|
||||||
"overlay-top": "Overlay top",
|
},
|
||||||
"overlay-left": "Overlay left",
|
"position": "Menu position",
|
||||||
"overlay-bottom": "Overlay bottom",
|
"positions": {
|
||||||
"overlay-right": "Overlay right",
|
"left": "Positioned on the left",
|
||||||
"hover-top": "Hover top",
|
"right": "Positioned on the right",
|
||||||
"hover-left": "Hover left",
|
"top": "Positioned on the top",
|
||||||
"hover-bottom": "Hover bottom",
|
"bottom": "Positioned on the bottom"
|
||||||
"hover-right": "Hover right",
|
},
|
||||||
"above": "Above",
|
"alignment": "Menu alignment",
|
||||||
"below": "Below"
|
"alignments": {
|
||||||
|
"left": "Aligned to the left",
|
||||||
|
"right": "Aligned to the right",
|
||||||
|
"top": "Aligned to the top",
|
||||||
|
"bottom": "Aligned to the bottom"
|
||||||
},
|
},
|
||||||
"button_size": "Menu button size in pixels"
|
"button_size": "Menu button size in pixels"
|
||||||
},
|
},
|
||||||
|
|||||||
+12
-8
@@ -29,11 +29,14 @@
|
|||||||
height: inherit;
|
height: inherit;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
-ms-overflow-style: none; /* Hide scrollbar: IE and Edge */
|
|
||||||
scrollbar-width: none; /* Hide scrollbar: Firefox */
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
// Hide scrollbar: Firefox
|
||||||
|
scrollbar-width: none;
|
||||||
|
// Hide scrollbar: IE and Edge
|
||||||
|
-ms-overflow-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||||
@@ -51,16 +54,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* The 'hover' menu mode is styling applied outside of the menu itself */
|
/* The 'hover' menu mode is styling applied outside of the menu itself */
|
||||||
frigate-card-menu[data-mode^=hover-] {
|
frigate-card-menu[data-style='hover'] {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
transition: opacity 0.5s ease;
|
transition: opacity 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.outer + frigate-card-menu[data-mode^=hover-] {
|
.outer + frigate-card-menu[data-style='hover'] {
|
||||||
opacity: 0.0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
.outer:hover + frigate-card-menu[data-mode^=hover-], frigate-card-menu[data-mode^=hover-]:hover {
|
.outer:hover + frigate-card-menu[data-style='hover'],
|
||||||
opacity: 1.0;
|
frigate-card-menu[data-mode='hover']:hover {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-card {
|
ha-card {
|
||||||
@@ -78,7 +82,7 @@ ha-card {
|
|||||||
border-radius: var(--ha-card-border-radius, 4px);
|
border-radius: var(--ha-card-border-radius, 4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
frigate-card-live.hidden {
|
frigate-card-live.hidden {
|
||||||
// Live view will be rendered but hidden for live preloading.
|
// Live view will be rendered but hidden for live preloading.
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
+53
-38
@@ -14,23 +14,41 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([data-mode="above"]),:host([data-mode="below"]) {
|
:host([data-style='outside']) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: var(--secondary-background-color);
|
background: var(--secondary-background-color);
|
||||||
}
|
}
|
||||||
:host([data-mode="above"]) {
|
:host([data-mode='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="below"]) {
|
:host([data-mode='outside'][data-position='below']) {
|
||||||
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-mode$="-left"]) {
|
:host([data-position='top']),
|
||||||
left: 0px;
|
:host([data-position='left'][data-alignment='top']),
|
||||||
|
:host([data-position='right'][data-alignment='top']) {
|
||||||
top: 0px;
|
top: 0px;
|
||||||
|
}
|
||||||
|
:host([data-position='bottom']),
|
||||||
|
:host([data-position='left'][data-alignment='bottom']),
|
||||||
|
:host([data-position='right'][data-alignment='bottom']) {
|
||||||
|
bottom: 0px;
|
||||||
|
}
|
||||||
|
:host([data-position='left']),
|
||||||
|
:host([data-position='top'][data-alignment='left']),
|
||||||
|
:host([data-position='bottom'][data-alignment='left']) {
|
||||||
|
left: 0px;
|
||||||
|
}
|
||||||
|
:host([data-position='right']),
|
||||||
|
:host([data-position='top'][data-alignment='right']),
|
||||||
|
:host([data-position='bottom'][data-alignment='right']) {
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([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
|
||||||
@@ -43,66 +61,63 @@
|
|||||||
// - 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([data-mode$="-right"]) {
|
|
||||||
right: 0px;
|
|
||||||
top: 0px;
|
|
||||||
|
|
||||||
// See "Awful hack" above.
|
// See "Awful hack" above.
|
||||||
writing-mode: vertical-rl;
|
writing-mode: vertical-rl;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([data-mode$="-top"]) {
|
:host([data-position='left'][data-alignment='bottom']),
|
||||||
left: 0px;
|
:host([data-position='right'][data-alignment='bottom']),
|
||||||
top: 0px;
|
:host([data-position='top'][data-alignment='right']),
|
||||||
|
:host([data-position='bottom'][data-alignment='right']) {
|
||||||
|
flex-direction: row-reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([data-mode$="-bottom"]) {
|
:host([data-position='bottom']) {
|
||||||
left: 0px;
|
|
||||||
bottom: 0px;
|
|
||||||
|
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([data-mode^="overlay-"]),:host([data-mode^="hover-"]),:host([data-mode^="hidden-"]) {
|
:host([data-style='overlay']),
|
||||||
|
:host([data-style='hover']),
|
||||||
|
:host([data-style='hidden']) {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: calc(var(--frigate-card-menu-button-size) + 6px);
|
width: calc(var(--frigate-card-menu-button-size) + 6px);
|
||||||
height: calc(var(--frigate-card-menu-button-size) + 6px);
|
height: calc(var(--frigate-card-menu-button-size) + 6px);
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([data-mode="overlay-top"]),
|
:host([data-style='overlay'][data-position='top']),
|
||||||
:host([data-mode="overlay-bottom"]),
|
:host([data-style='overlay'][data-position='bottom']),
|
||||||
:host([data-mode="hover-top"]),
|
:host([data-style='hover'][data-position='top']),
|
||||||
:host([data-mode="hover-bottom"]),
|
:host([data-style='hover'][data-position='bottom']),
|
||||||
:host([data-mode="hidden-top"][expanded]),
|
:host([data-style='hidden'][data-position='top'][expanded]),
|
||||||
:host([data-mode="hidden-bottom"][expanded]) {
|
:host([data-style='hidden'][data-position='bottom'][expanded]) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
background: linear-gradient(90deg, rgba(0,0,0,0.3), rgba(0,0,0,0));
|
background: linear-gradient(90deg, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([data-mode="overlay-left"]),
|
:host([data-style='overlay'][data-position='left']),
|
||||||
:host([data-mode="overlay-right"]),
|
:host([data-style='overlay'][data-position='right']),
|
||||||
:host([data-mode="hover-left"]),
|
:host([data-style='hover'][data-position='left']),
|
||||||
:host([data-mode="hover-right"]),
|
:host([data-style='hover'][data-position='right']),
|
||||||
:host([data-mode="hidden-left"][expanded]),
|
:host([data-style='hidden'][data-position='left'][expanded]),
|
||||||
:host([data-mode="hidden-right"][expanded]) {
|
:host([data-style='hidden'][data-position='right'][expanded]) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: auto;
|
width: auto;
|
||||||
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-mode="overlay-left"]) > *,
|
:host([data-style='overlay'][data-position='left']) > *,
|
||||||
:host([data-mode="hover-left"]) > *,
|
:host([data-style='overlay'][data-position='right']) > *,
|
||||||
:host([data-mode="hidden-left"]) > *,
|
:host([data-style='hover'][data-position='left']) > *,
|
||||||
:host([data-mode="overlay-right"]) > *,
|
:host([data-style='hover'][data-position='right']) > *,
|
||||||
:host([data-mode="hover-right"]) > *,
|
:host([data-style='hidden'][data-position='left']) > *,
|
||||||
:host([data-mode="hidden-right"]) > * {
|
:host([data-style='hidden'][data-position='right']) > * {
|
||||||
// See "Awful hack" above.
|
// See "Awful hack" above.
|
||||||
writing-mode: horizontal-tb;
|
writing-mode: horizontal-tb;
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-17
@@ -53,23 +53,21 @@ const FRIGATE_CARD_VIEWS = [
|
|||||||
|
|
||||||
export type FrigateCardView = typeof FRIGATE_CARD_VIEWS[number];
|
export type FrigateCardView = typeof FRIGATE_CARD_VIEWS[number];
|
||||||
|
|
||||||
const FRIGATE_MENU_MODES = [
|
const FRIGATE_MENU_STYLES = [
|
||||||
'none',
|
'none',
|
||||||
'hidden-top',
|
'hidden',
|
||||||
'hidden-left',
|
'overlay',
|
||||||
'hidden-bottom',
|
'hover',
|
||||||
'hidden-right',
|
'outside',
|
||||||
'overlay-top',
|
|
||||||
'overlay-left',
|
|
||||||
'overlay-bottom',
|
|
||||||
'overlay-right',
|
|
||||||
'hover-top',
|
|
||||||
'hover-left',
|
|
||||||
'hover-bottom',
|
|
||||||
'hover-right',
|
|
||||||
'above',
|
|
||||||
'below',
|
|
||||||
] as const;
|
] as const;
|
||||||
|
const FRIGATE_MENU_POSITIONS = [
|
||||||
|
'left',
|
||||||
|
'right',
|
||||||
|
'top',
|
||||||
|
'bottom',
|
||||||
|
] as const;
|
||||||
|
const FRIGATE_MENU_ALIGNMENTS = FRIGATE_MENU_POSITIONS;
|
||||||
|
|
||||||
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];
|
||||||
|
|
||||||
@@ -615,7 +613,9 @@ export type LiveConfig = z.infer<typeof liveConfigSchema>;
|
|||||||
* Menu configuration section.
|
* Menu configuration section.
|
||||||
*/
|
*/
|
||||||
const menuConfigDefault = {
|
const menuConfigDefault = {
|
||||||
mode: 'hidden-top' as const,
|
style: 'hidden' as const,
|
||||||
|
position: 'top' as const,
|
||||||
|
alignment: 'left' as const,
|
||||||
buttons: {
|
buttons: {
|
||||||
frigate: true,
|
frigate: true,
|
||||||
cameras: true,
|
cameras: true,
|
||||||
@@ -633,7 +633,9 @@ const menuConfigDefault = {
|
|||||||
|
|
||||||
const menuConfigSchema = z
|
const menuConfigSchema = z
|
||||||
.object({
|
.object({
|
||||||
mode: z.enum(FRIGATE_MENU_MODES).default(menuConfigDefault.mode),
|
style: z.enum(FRIGATE_MENU_STYLES).default(menuConfigDefault.style),
|
||||||
|
position: z.enum(FRIGATE_MENU_POSITIONS).default(menuConfigDefault.position),
|
||||||
|
alignment: z.enum(FRIGATE_MENU_ALIGNMENTS).default(menuConfigDefault.alignment),
|
||||||
buttons: z
|
buttons: z
|
||||||
.object({
|
.object({
|
||||||
frigate: z.boolean().default(menuConfigDefault.buttons.frigate),
|
frigate: z.boolean().default(menuConfigDefault.buttons.frigate),
|
||||||
|
|||||||
Reference in New Issue
Block a user