diff --git a/docs/configuration/cameras/README.md b/docs/configuration/cameras/README.md index d42f1b55..5319c20b 100644 --- a/docs/configuration/cameras/README.md +++ b/docs/configuration/cameras/README.md @@ -264,11 +264,11 @@ cameras: Not all [engines](./engine.md) benefit from proxying: -| Engine | Purpose of proxying | -| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `frigate` | The Frigate integration already comes with a built-in proxy, so this functionality does not serve any purpose for `frigate`. | -| `reolink`, `motioneEye` | May be used to fetch videos in cases where the browser may not be able to access the camera/NVR, or the camera/NVR may use a self-signed SSL certificate that your browser would otherwise reject due to [mixed content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content). | -| `generic` | `generic` cameras do not have media, so proxying currently would serve no purpose. | +| Engine | Purpose of proxying | +| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `frigate` | The Frigate integration already comes with a built-in proxy, so this functionality does not serve any purpose for `frigate`. | +| `reolink`, `motioneye` | May be used to fetch videos in cases where the browser may not be able to access the camera/NVR, or the camera/NVR may use a self-signed SSL certificate that your browser would otherwise reject due to [mixed content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content). | +| `generic` | `generic` cameras do not have media, so proxying currently would serve no purpose. | Regardless of the parameters, the integration will never attempt to proxy content if the diff --git a/src/components-lib/media-grid-controller.ts b/src/components-lib/media-grid-controller.ts index 353ef331..1ca2c48e 100644 --- a/src/components-lib/media-grid-controller.ts +++ b/src/components-lib/media-grid-controller.ts @@ -21,6 +21,7 @@ import { const MEDIA_GRID_DEFAULT_MIN_CELL_WIDTH = 190; const MEDIA_GRID_DEFAULT_IDEAL_CELL_WIDTH = 600; const MEDIA_GRID_DEFAULT_SELECTED_WIDTH_FACTOR = 2.0; +const MEDIA_GRID_HORIZONTAL_GUTTER_WIDTH = 1; type GridID = string; type MediaGridChild = HTMLElement & FrigateCardMediaLoadedEventTarget; @@ -276,6 +277,7 @@ export class MediaGridController { initLayout: false, percentPosition: true, transitionDuration: '0.2s', + gutter: MEDIA_GRID_HORIZONTAL_GUTTER_WIDTH, }); this._masonry.addItems?.([...this._gridContents.values()]); this._throttledLayout(); @@ -308,7 +310,12 @@ export class MediaGridController { } protected _getColumnSize(): number { - return Math.round(this._hostWidth / this._getColumns()); + const columns = this._getColumns(); + if (columns === 1) { + return this._hostWidth; + } + + return Math.max(0, this._hostWidth / columns - MEDIA_GRID_HORIZONTAL_GUTTER_WIDTH); } protected _getColumns(): number { diff --git a/tests/components-lib/media-grid-controller.test.ts b/tests/components-lib/media-grid-controller.test.ts index bc760878..bac8bad1 100644 --- a/tests/components-lib/media-grid-controller.test.ts +++ b/tests/components-lib/media-grid-controller.test.ts @@ -398,16 +398,16 @@ describe('MediaGridController', () => { expect(Masonry).toBeCalledWith( parent, expect.objectContaining({ - columnWidth: 246, + columnWidth: 245, }), ); expect(parent.style.getPropertyValue('--frigate-card-grid-column-size')).toBe( - '246px', + '245px', ); }); it('should respect exact columns', () => { - const parent = createParent({ children: createChildren(), width: 2000 }); + const parent = createParent({ children: createChildren(), width: 3000 }); const controller = createController(parent); controller.setDisplayConfig({ mode: 'grid', grid_columns: 2 }); @@ -417,11 +417,11 @@ describe('MediaGridController', () => { expect(Masonry).toBeCalledWith( parent, expect.objectContaining({ - columnWidth: 1000, + columnWidth: 1499, }), ); expect(parent.style.getPropertyValue('--frigate-card-grid-column-size')).toBe( - '1000px', + '1499px', ); }); @@ -487,11 +487,11 @@ describe('MediaGridController', () => { expect(Masonry).toBeCalledWith( parent, expect.objectContaining({ - columnWidth: 246, + columnWidth: 245, }), ); expect(parent.style.getPropertyValue('--frigate-card-grid-column-size')).toBe( - '246px', + '245px', ); // Clear mock state. @@ -499,18 +499,18 @@ describe('MediaGridController', () => { vi.mocked(masonry.layout)?.mockClear(); // Resize the host. - setElementWidth(parent, 2000); + setElementWidth(parent, 3000); triggerResizeObserver('host'); // Masonry should be reconstructed, styles set and layout called. expect(Masonry).toBeCalledWith( parent, expect.objectContaining({ - columnWidth: 667, + columnWidth: 599, }), ); expect(parent.style.getPropertyValue('--frigate-card-grid-column-size')).toBe( - '667px', + '599px', ); expect(masonry.layout).toBeCalled();