From 0e751478d250e7ab4bf82778c65fb52543347f4d Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Sat, 16 Apr 2022 21:59:36 -0700 Subject: [PATCH] Fix thumbnails for non-gallery views. --- README.md | 12 ++++++------ src/components/gallery.ts | 3 +-- src/components/menu.ts | 5 ++++- src/components/thumbnail.ts | 10 +++------- src/editor.ts | 33 ++++++++++++++++++++++++-------- src/localize/languages/en.json | 12 ++++++------ src/scss/const.scss | 5 +++++ src/scss/gallery.scss | 3 ++- src/scss/thumbnail-carousel.scss | 11 +++++++++++ src/scss/thumbnail-details.scss | 23 ++++++++++++++++++---- src/scss/thumbnail.scss | 9 +++++---- src/types.ts | 7 ++++--- 12 files changed, 91 insertions(+), 42 deletions(-) create mode 100644 src/scss/const.scss diff --git a/README.md b/README.md index 07d8695e..8d970c79 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ menu: | Option | Default | Overridable | Description | | - | - | - | - | | `mode` | `hidden-top` | :white_check_mark: | The menu mode to show by default. See [menu modes](#menu-modes) below.| -| `button_size` | `40px` | :white_check_mark: | The size of the menu buttons [in CSS Units](https://www.w3schools.com/cssref/css_units.asp).| +| `button_size` | 40 | :white_check_mark: | The size of the menu buttons in pixels.| | `buttons` | | :white_check_mark: | Whether to show or hide built-in buttons. See below. | #### Menu Options: Buttons @@ -258,7 +258,7 @@ live: | Option | Default | Overridable | Description | | - | - | - | - | | `mode` | `none` | :white_check_mark: | Whether to show the thumbnail carousel `below` the media, `above` the media, in a drawer to the `left` or `right` of the media or to hide it entirely (`none`).| -| `size` | `100px` | :white_check_mark: | The size of the thumbnails in the thumbnail carousel [in CSS Units](https://www.w3schools.com/cssref/css_units.asp).| +| `size` | 100 | :white_check_mark: | The size of the thumbnails in the thumbnail carousel in pixels.| | `show_details` | `false` | :white_check_mark: | Whether to show event details (e.g. duration, start time, object detected, etc) alongside the thumbnail.| | `show_controls` | `true` | :white_check_mark: | Whether to show event controls (e.g. timeline icon, favorite icon) alongside the thumbnail.| | `media` | `clips` | :white_check_mark: | Whether to show `clips` or `snapshots` in the thumbnail carousel in the `live` view.| @@ -276,7 +276,7 @@ live: | Option | Default | Overridable | Description | | - | - | - | - | | `style` | `chevrons` | :white_check_mark: | When viewing live cameras, what kind of controls to show to move to the previous/next camera. Acceptable values: `chevrons`, `icons`, `none` . | -| `size` | `48px` | :white_check_mark: | The size of the next/previous controls [in CSS Units](https://www.w3schools.com/cssref/css_units.asp).| +| `size` | 48 | :white_check_mark: | The size of the next/previous controls in pixels.| @@ -328,7 +328,7 @@ event_viewer: | Option | Default | Overridable | Description | | - | - | - | - | | `style` | `thumbnails` | :heavy_multiplication_x: | When viewing media, what kind of controls to show to move to the previous/next media item. Acceptable values: `thumbnails`, `chevrons`, `none` . | -| `size` | `48px` | :heavy_multiplication_x: | The size of the next/previous controls [in CSS Units](https://www.w3schools.com/cssref/css_units.asp).| +| `size` | 48 | :heavy_multiplication_x: | The size of the next/previous controls in pixels.| #### Event Viewer Controls: Thumbnails @@ -343,7 +343,7 @@ event_viewer: | Option | Default | Overridable | Description | | - | - | - | - | | `mode` | `none` | :heavy_multiplication_x: | Whether to show the thumbnail carousel `below` the media, `above` the media, in a drawer to the `left` or `right` of the media or to hide it entirely (`none`).| -| `size` | `100px` | :heavy_multiplication_x: | The size of the thumbnails in the thumbnail carousel [in CSS Units](https://www.w3schools.com/cssref/css_units.asp).| +| `size` | 100 | :heavy_multiplication_x: | The size of the thumbnails in the thumbnail carousel pixels.| | `show_details` | `false` | :heavy_multiplication_x: | Whether to show event details (e.g. duration, start time, object detected, etc) alongside the thumbnail.| | `show_controls` | `true` | :heavy_multiplication_x: | Whether to show event controls (e.g. timeline icon, favorite icon) alongside the thumbnail.| @@ -423,7 +423,7 @@ timeline: | Option | Default | Overridable | Description | | - | - | - | - | | `mode` | `none` | :heavy_multiplication_x: | Whether to show the thumbnail carousel `below` the media, `above` the media, in a drawer to the `left` or `right` of the media or to hide it entirely (`none`).| -| `size` | `100px` | :heavy_multiplication_x: | The size of the thumbnails in the thumbnail carousel [in CSS Units](https://www.w3schools.com/cssref/css_units.asp).| +| `size` | 100 | :heavy_multiplication_x: | The size of the thumbnails in the thumbnail carousel in pixels.| | `show_details` | `false` | :heavy_multiplication_x: | Whether to show event details (e.g. duration, start time, object detected, etc) alongside the thumbnail.| | `show_controls` | `true` | :heavy_multiplication_x: | Whether to show event controls (e.g. timeline icon, favorite icon) alongside the thumbnail.| diff --git a/src/components/gallery.ts b/src/components/gallery.ts index e3960962..8509fa0b 100644 --- a/src/components/gallery.ts +++ b/src/components/gallery.ts @@ -1,6 +1,5 @@ // TODO: Add details & controls & size support -// TODO: Remove mini support if it's not actually needed? -// TODO: automatic upgrade for thumbnail sizes in px +// TODO: automatic upgrade for thumbnail sizes in px and menu buttons in px // TODO: automatic remove of min_columns /* eslint-disable @typescript-eslint/no-explicit-any */ diff --git a/src/components/menu.ts b/src/components/menu.ts index 51af65a6..b253c47c 100644 --- a/src/components/menu.ts +++ b/src/components/menu.ts @@ -60,7 +60,10 @@ export class FrigateCardMenu extends LitElement { set menuConfig(menuConfig: MenuConfig) { this._menuConfig = menuConfig; if (menuConfig) { - this.style.setProperty('--frigate-card-menu-button-size', menuConfig.button_size); + this.style.setProperty( + '--frigate-card-menu-button-size', + `${menuConfig.button_size}px`, + ); } // Store the menu mode as an attribute (used for CSS attribute selectors). this.setAttribute('data-mode', menuConfig.mode); diff --git a/src/components/thumbnail.ts b/src/components/thumbnail.ts index 60bc9906..4a4480a4 100644 --- a/src/components/thumbnail.ts +++ b/src/components/thumbnail.ts @@ -30,20 +30,16 @@ export class FrigateCardThumbnailDetails extends LitElement { return html`
${prettifyFrigateName(this.event.label)}
- ${localize('event.start')}: - ${format(fromUnixTime(this.event.start_time), 'HH:mm:ss')} - + ${format(fromUnixTime(this.event.start_time), 'HH:mm:ss')}
- ${localize('event.duration')}: - ${getEventDurationString(this.event)} - + ${getEventDurationString(this.event)}
-
${score}
+ ${score}
`; } diff --git a/src/editor.ts b/src/editor.ts index f74e9a77..1bf47554 100644 --- a/src/editor.ts +++ b/src/editor.ts @@ -5,9 +5,11 @@ import { customElement, property, state } from 'lit/decorators.js'; import { HomeAssistant, LovelaceCardEditor, fireEvent } from 'custom-card-helpers'; import { localize } from './localize/localize.js'; import { - frigateCardConfigDefaults, RawFrigateCardConfig, RawFrigateCardConfigArray, + THUMBNAIL_WIDTH_MAX, + THUMBNAIL_WIDTH_MIN, + frigateCardConfigDefaults, } from './types.js'; import { @@ -843,7 +845,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor ? html`
${this._renderOptionSelector(CONF_MENU_MODE, this._menuModes)} - ${this._renderStringInput(CONF_MENU_BUTTON_SIZE)} + ${this._renderNumberInput(CONF_MENU_BUTTON_SIZE)} ${this._renderSwitch( CONF_MENU_BUTTONS_FRIGATE, defaults.menu.buttons.frigate, @@ -900,7 +902,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor CONF_LIVE_CONTROLS_NEXT_PREVIOUS_STYLE, this._liveNextPreviousControlStyles, )} - ${this._renderStringInput(CONF_LIVE_CONTROLS_NEXT_PREVIOUS_SIZE)} + ${this._renderNumberInput(CONF_LIVE_CONTROLS_NEXT_PREVIOUS_SIZE)} ${this._renderOptionSelector( CONF_LIVE_CONTROLS_THUMBNAILS_MODE, this._thumbnailModes, @@ -909,7 +911,11 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor CONF_LIVE_CONTROLS_THUMBNAILS_MEDIA, this._thumbnailMedias, )} - ${this._renderStringInput(CONF_LIVE_CONTROLS_THUMBNAILS_SIZE)} + ${this._renderNumberInput( + CONF_LIVE_CONTROLS_THUMBNAILS_SIZE, + THUMBNAIL_WIDTH_MIN, + THUMBNAIL_WIDTH_MAX, + )} ${this._renderOptionSelector( CONF_LIVE_CONTROLS_TITLE_MODE, this._titleModes, @@ -963,12 +969,16 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE, this._eventViewerNextPreviousControlStyles, )} - ${this._renderStringInput(CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_SIZE)} + ${this._renderNumberInput(CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_SIZE)} ${this._renderOptionSelector( CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_MODE, this._thumbnailModes, )} - ${this._renderStringInput(CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_SIZE)} + ${this._renderNumberInput( + CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_SIZE, + THUMBNAIL_WIDTH_MIN, + THUMBNAIL_WIDTH_MAX, + )} ${this._renderSwitch( CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_SHOW_DETAILS, defaults.event_viewer.controls.thumbnails.show_details, @@ -1005,12 +1015,19 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor ? html`
${this._renderNumberInput(CONF_TIMELINE_WINDOW_SECONDS)} ${this._renderNumberInput(CONF_TIMELINE_CLUSTERING_THRESHOLD)} - ${this._renderOptionSelector(CONF_TIMELINE_MEDIA, this._timelineMediaTypes)} + ${this._renderOptionSelector( + CONF_TIMELINE_MEDIA, + this._timelineMediaTypes, + )} ${this._renderOptionSelector( CONF_TIMELINE_CONTROLS_THUMBNAILS_MODE, this._thumbnailModes, )} - ${this._renderStringInput(CONF_TIMELINE_CONTROLS_THUMBNAILS_SIZE)} + ${this._renderNumberInput( + CONF_TIMELINE_CONTROLS_THUMBNAILS_SIZE, + THUMBNAIL_WIDTH_MIN, + THUMBNAIL_WIDTH_MAX, + )} ${this._renderSwitch( CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_DETAILS, defaults.timeline.controls.thumbnails.show_details, diff --git a/src/localize/languages/en.json b/src/localize/languages/en.json index a85aac1c..04903fd0 100644 --- a/src/localize/languages/en.json +++ b/src/localize/languages/en.json @@ -66,7 +66,7 @@ "controls": { "next_previous": { "style": "Event Viewer next & previous control style", - "size": "Event Viewer next & previous control size (e.g. '48px')", + "size": "Event Viewer next & previous control size in pixels", "styles": { "thumbnails": "Thumbnails", "chevrons": "Chevrons", @@ -75,7 +75,7 @@ }, "thumbnails": { "mode": "Event Viewer thumbnails mode", - "size": "Event Viewer thumbnails size (e.g. '100px')", + "size": "Event Viewer thumbnails size in pixels", "show_details": "Show event details with thumbnails", "show_controls": "Show event controls with thumbnails", "modes": { @@ -109,7 +109,7 @@ "controls": { "next_previous": { "style": "Live view next & previous control style", - "size": "Live view next & previous control size (e.g. '48px')", + "size": "Live view next & previous control size in pixels", "styles": { "chevrons": "Chevrons", "icons": "Icons", @@ -118,7 +118,7 @@ }, "thumbnails": { "mode": "Live thumbnails mode", - "size": "Live thumbnails size (e.g. '100px')", + "size": "Live thumbnails size in pixels", "show_details": "Show event details with thumbnails", "show_controls": "Show event controls with thumbnails", "media": "Whether to show thumbnails of clips or snapshots", @@ -169,7 +169,7 @@ "above": "Above", "below": "Below" }, - "button_size": "Menu button size (e.g. '40px')" + "button_size": "Menu button size in pixels" }, "timeline": { "window_seconds": "The default length of the timeline view in seconds", @@ -183,7 +183,7 @@ "controls": { "thumbnails": { "mode": "Timeline thumbnails mode", - "size": "Timeline thumbnails size (e.g. '100px')", + "size": "Timeline thumbnails size in pixels", "show_details": "Show event details with thumbnails", "show_controls": "Show event controls with thumbnails" } diff --git a/src/scss/const.scss b/src/scss/const.scss new file mode 100644 index 00000000..38cc2113 --- /dev/null +++ b/src/scss/const.scss @@ -0,0 +1,5 @@ +:host { + --frigate-card-thumbnail-size: 100px; + --frigate-card-thumbnail-size-max: 175px; + --frigate-card-thumbnail-details-width: calc(var(--frigate-card-thumbnail-size) + 200px); +} \ No newline at end of file diff --git a/src/scss/gallery.scss b/src/scss/gallery.scss index d47a4fdc..2777fd32 100644 --- a/src/scss/gallery.scss +++ b/src/scss/gallery.scss @@ -1,3 +1,5 @@ +@use "const.scss"; + :host { width: 100%; height: 100%; @@ -11,7 +13,6 @@ --frigate-card-gallery-gap: 3px; --frigate-card-gallery-columns: 4; - --frigate-card-thumbnail-size: 100px; display: grid; grid-template-columns: repeat(var(--frigate-card-gallery-columns), minmax(0, 1fr)); diff --git a/src/scss/thumbnail-carousel.scss b/src/scss/thumbnail-carousel.scss index bf20b658..033d09fd 100644 --- a/src/scss/thumbnail-carousel.scss +++ b/src/scss/thumbnail-carousel.scss @@ -1,3 +1,5 @@ +@use "const.scss"; + :host { --frigate-card-carousel-thumbnail-opacity: 1.0; } @@ -18,4 +20,13 @@ } .embla__slide.slide-selected { opacity: 1.0; +} + +frigate-card-thumbnail[details] { + width: var(--frigate-card-thumbnail-details-width); + height: var(--frigate-card-thumbnail-size); +} +frigate-card-thumbnail:not([details]) { + width: var(--frigate-card-thumbnail-size); + height: var(--frigate-card-thumbnail-size); } \ No newline at end of file diff --git a/src/scss/thumbnail-details.scss b/src/scss/thumbnail-details.scss index 33cf1e70..df77a6c2 100644 --- a/src/scss/thumbnail-details.scss +++ b/src/scss/thumbnail-details.scss @@ -2,12 +2,13 @@ display: flex; width: 100%; margin-left: 5px; - padding: 15px; + padding: 8px; color: var(--primary-text-color); overflow: hidden; + column-gap: 5%; } -div { +div.right, div.left { display: flex; flex-direction: column; justify-content: center; @@ -15,12 +16,26 @@ div { div.left { flex: 1; + + display: flex; + flex-direction: column; + flex-wrap: wrap; + align-content: flex-start; + justify-content: center; + + overflow: hidden; + + column-gap: 5%; } -div.larger { - font-size: 1.5rem; +div.left div { + width: 100%; } span.heading { font-weight: bold; } + +.larger { + font-size: 1.5rem; +} diff --git a/src/scss/thumbnail.scss b/src/scss/thumbnail.scss index c37f33dc..5eaf92c4 100644 --- a/src/scss/thumbnail.scss +++ b/src/scss/thumbnail.scss @@ -1,3 +1,5 @@ +@use "const.scss"; + :host { display: flex; flex-direction: row; @@ -5,8 +7,6 @@ // Ensure control icons are relative to the thumbnail. position: relative; - - --frigate-card-max-thumbnail-size: 175px; } :host([details]) { @@ -29,9 +29,10 @@ img { // Restrict images to a maximum of thumbnail size. aspect-ratio: 1 / 1; + height: 100%; - max-width: var(--frigate-card-max-thumbnail-size); - max-height: var(--frigate-card-max-thumbnail-size); + max-width: var(--frigate-card-thumbnail-size-max); + max-height: var(--frigate-card-thumbnail-size-max); transition: transform 0.2s linear; } diff --git a/src/types.ts b/src/types.ts index 2e357ddd..5a27e7d9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -16,6 +16,7 @@ import { deepRemoveDefaults } from './zod-util'; // The maximum width thumbnail Frigate returns export const THUMBNAIL_WIDTH_MAX = 175; +export const THUMBNAIL_WIDTH_MIN = 75; declare global { interface HTMLElementTagNameMap { @@ -442,7 +443,7 @@ export type ImageViewConfig = z.infer; const thumbnailsControlSchema = z.object({ mode: z.enum(['none', 'above', 'below', 'left', 'right']), - size: z.number().min(1).max(THUMBNAIL_WIDTH_MAX).optional(), + size: z.number().min(THUMBNAIL_WIDTH_MIN).max(THUMBNAIL_WIDTH_MAX).optional(), show_details: z.boolean().optional(), show_controls: z.boolean().optional(), }); @@ -617,7 +618,7 @@ const menuConfigDefault = { frigate_ui: true, fullscreen: true, }, - button_size: '40px', + button_size: 40, }; const menuConfigSchema = z @@ -637,7 +638,7 @@ const menuConfigSchema = z fullscreen: z.boolean().default(menuConfigDefault.buttons.fullscreen), }) .default(menuConfigDefault.buttons), - button_size: z.string().default(menuConfigDefault.button_size), + button_size: z.number().min(1).default(menuConfigDefault.button_size), }) .default(menuConfigDefault); export type MenuConfig = z.infer;