diff --git a/README.md b/README.md index 83919295..629cfc25 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ All variables listed are under a `live:` section. ### Event Viewer options -The `event_viewer` is used for viewing all `clip` and `snapshot` media, in a media carousel. All variables listed are under a `event_viewer:` section. +The `event_viewer` is used for viewing all `clip` and `snapshot` media, in a media carousel. All variables listed are under an `event_viewer:` section. | Option | Default | Description | | - | - | - | @@ -503,10 +503,10 @@ This card supports several different views: | Key | Description | | ------------- | --------------------------------------------- | |`live` (default)| Shows the live camera view, either the name Frigate view or [WebRTC](#webrtc) if configured.| -|`snapshots`|Shows the snapshot gallery for this camera/zone/label.| -|`snapshot`|Shows the most recent snapshot for this camera/zone/label. Can also be accessed by holding down the `snapshots` menu icon.| -|`clips`|Shows the clip gallery for this camera/zone/label.| -|`clip`|Shows the most recent clip for this camera/zone/label. Can also be accessed by holding down the `clips` menu icon.| +|`snapshots`|Shows an event gallery of snapshots for this camera/zone/label.| +|`snapshot`|Shows an event viewer for the most recent snapshot for this camera/zone/label. Can also be accessed by holding down the `snapshots` menu icon.| +|`clips`|Shows an event gallery of clips for this camera/zone/label.| +|`clip`|Shows an event viewer for the most recent clip for this camera/zone/label. Can also be accessed by holding down the `clips` menu icon.| |`image`|Shows a static image specified by the `image` parameter, can be used as a discrete default view or a screensaver (via `view_timeout`).| ### Automatic updates in the `clip` or `snapshot` view @@ -552,7 +552,15 @@ specific overriding the less specific (see example below). The format for actions is the standard Home Assistant [action format](https://www.home-assistant.io/lovelace/actions/#tap-action) as well as -the custom [Frigate card action](#frigate-card-action) to trigger card changes. +the custom [Frigate card action](#frigate-card-action) to trigger Frigate card +changes. + +**Note:** The card itself obviously relies on human interactions to function +(e.g. `tap` on the menu should activate that button, `tap` on a gallery thumbnail +should open that piece of media, etc). These internal actions are executed +_also_, which means that a card-wide `tap` action probably isn't that useful as +it may be disorienting to the user and will trigger on all kinds of basic +interaction on the card (e.g. tapping/clicking a menu button). ### Example diff --git a/src/card.ts b/src/card.ts index a0501ccf..270c3836 100644 --- a/src/card.ts +++ b/src/card.ts @@ -212,6 +212,11 @@ export class FrigateCard extends LitElement { } as FrigateCardConfig; } + /** + * Get a FrigateCard MenuButton given a set of parameters. + * @param params Menu button parameters. + * @returns A MenuButton. + */ protected _getFrigateCardMenuButton( params: GetFrigateCardMenuButtonParameters, ): MenuButton { @@ -598,10 +603,14 @@ export class FrigateCard extends LitElement { /** * Handle a request for a card action. - * @param action The action requested (e.g. clips, fullscreen) + * @param ev The action requested. */ - protected _cardActionHandler(event: CustomEvent): void { - const frigateCardAction = convertActionToFrigateCardCustomAction(event.detail); + protected _cardActionHandler(ev: CustomEvent): void { + // These interactions should only be handled by the card, as nothing + // upstream has the user-provided configuration. + ev.stopPropagation(); + + const frigateCardAction = convertActionToFrigateCardCustomAction(ev.detail); if (!frigateCardAction) { return; } @@ -670,6 +679,10 @@ export class FrigateCard extends LitElement { }, this.config.view.timeout * 1000); } + /** + * Handle an action called on an element. + * @param ev The actionHandler event. + */ protected _actionHandler( ev: CustomEvent, config?: { @@ -866,6 +879,10 @@ export class FrigateCard extends LitElement { } } + /** + * Merge card-wide and view-specific actions. + * @returns A combined set of action. + */ protected _getMergedActions(): Actions { let specificActions: Actions | undefined = undefined; diff --git a/src/common.ts b/src/common.ts index 429b0ca4..a87240aa 100644 --- a/src/common.ts +++ b/src/common.ts @@ -258,6 +258,11 @@ export function isValidMediaShowInfo(info: MediaShowInfo): boolean { ); } +/** + * Convert a generic Action to a FrigateCardCustomAction if it parses correctly. + * @param action The generic action configuration. + * @returns A FrigateCardCustomAction or null if it cannot be converted. + */ export function convertActionToFrigateCardCustomAction( action: ActionType, ): FrigateCardCustomAction | null { @@ -267,6 +272,11 @@ export function convertActionToFrigateCardCustomAction( return parseResult.success ? parseResult.data : null; } +/** + * Create a Frigate card custom action. + * @param action The Frigate card action string (e.g. 'fullscreen') + * @returns A FrigateCardCustomAction for that action string. + */ export function createFrigateCardCustomAction(action: string): FrigateCardCustomAction { return { action: 'fire-dom-event', @@ -274,6 +284,12 @@ export function createFrigateCardCustomAction(action: string): FrigateCardCustom }; } +/** + * Get an action configuration given a config and an interaction (e.g. 'tap'). + * @param interaction The interaction: `tap`, `hold` or `double_tap` + * @param config The configuration containing multiple actions. + * @returns The relevant action configuration or null if none found. + */ export function getActionConfigGivenAction( interaction?: string, config?: { diff --git a/src/components/menu.ts b/src/components/menu.ts index b6beeb4d..a3468bb4 100644 --- a/src/components/menu.ts +++ b/src/components/menu.ts @@ -30,7 +30,9 @@ import menuStyle from '../scss/menu.scss'; export const MENU_HEIGHT = 46; export const FRIGATE_BUTTON_MENU_ICON = 'frigate'; -// A menu for the Frigate card. +/** + * A menu for the FrigateCard. + */ @customElement('frigate-card-menu') export class FrigateCardMenu extends LitElement { @property({ attribute: false }) @@ -51,14 +53,23 @@ export class FrigateCardMenu extends LitElement { @property({ attribute: false }) public buttons: MenuButton[] = []; + /** + * Handle an action on a menu button. + * @param ev The action event. + * @param button The button configuration. + */ protected _actionHandler(ev: CustomEvent, button: MenuButton): void { if (!ev) { return; } + // These interactions should only be handled by the card, as nothing + // upstream has the user-provided configuration. + ev.stopPropagation(); + const interaction: string = ev.detail.action; const action = getActionConfigGivenAction(interaction, button); - if (!action) { + if (!action || !interaction) { return; } @@ -81,7 +92,11 @@ export class FrigateCardMenu extends LitElement { handleAction(this, this.hass as HomeAssistant, button, interaction); } - // Determine whether the menu should be updated. + /** + * Determine whether the menu should be updated. + * @param changedProps The changed properties. + * @returns `true` if the menu should be updated, otherwise `false`. + */ protected shouldUpdate(changedProps: PropertyValues): boolean { const oldHass = changedProps.get('hass') as HomeAssistant | undefined; @@ -100,13 +115,21 @@ export class FrigateCardMenu extends LitElement { return shouldUpdateBasedOnHass(this.hass, oldHass, entities); } + /** + * Get the style of emphasized menu items. + * @returns A StyleInfo. + */ public static getEmphasizedStyle(): StyleInfo { return { color: 'var(--primary-color, white)', }; } - // Render a menu button. + /** + * Render a button. + * @param button The button configuration to render. + * @returns A rendered template or void. + */ protected _renderButton(button: MenuButton): TemplateResult | void { let state: HassEntity | null = null; let title = button.title; @@ -163,7 +186,10 @@ export class FrigateCardMenu extends LitElement { `; } - // Render the menu. + /** + * Render the menu. + * @returns A rendered template or void. + */ protected render(): TemplateResult | void { if (!this._menuConfig) { return; @@ -200,8 +226,10 @@ export class FrigateCardMenu extends LitElement { `; } - // Return compiled CSS styles (thus safe to use with unsafeCSS). - static get styles(): CSSResultGroup { + /** + * Get styles. + */ + static get styles(): CSSResultGroup { return unsafeCSS(menuStyle); } }