diff --git a/web/package-lock.json b/web/package-lock.json index 1244dcc7c5..7792fb0956 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -11,6 +11,7 @@ "axios": "^0.27.2", "copy-image-clipboard": "^2.1.2", "handlebars": "^4.7.7", + "justified-layout": "^4.1.0", "leaflet": "^1.9.3", "lodash-es": "^4.17.21", "luxon": "^3.2.1", @@ -28,6 +29,7 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/svelte": "^3.2.2", "@types/cookie": "^0.5.1", + "@types/justified-layout": "^4.1.0", "@types/leaflet": "^1.9.1", "@types/lodash-es": "^4.17.6", "@types/luxon": "^3.2.0", @@ -3605,6 +3607,12 @@ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, + "node_modules/@types/justified-layout": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@types/justified-layout/-/justified-layout-4.1.0.tgz", + "integrity": "sha512-D8u3yfJjx1xjRJxWUVJlTmFxSN0ph5BpkQo9dTIO1LpYFu0WS1XcWJoNAKJZql+jyiZB5eHt0UKxC1wsmO3/LQ==", + "dev": true + }, "node_modules/@types/leaflet": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.1.tgz", @@ -9008,6 +9016,11 @@ "node": ">=6" } }, + "node_modules/justified-layout": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/justified-layout/-/justified-layout-4.1.0.tgz", + "integrity": "sha512-M5FimNMXgiOYerVRGsXZ2YK9YNCaTtwtYp7Hb2308U1Q9TXXHx5G0p08mcVR5O53qf8bWY4NJcPBxE6zuayXSg==" + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -14027,6 +14040,12 @@ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, + "@types/justified-layout": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@types/justified-layout/-/justified-layout-4.1.0.tgz", + "integrity": "sha512-D8u3yfJjx1xjRJxWUVJlTmFxSN0ph5BpkQo9dTIO1LpYFu0WS1XcWJoNAKJZql+jyiZB5eHt0UKxC1wsmO3/LQ==", + "dev": true + }, "@types/leaflet": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.1.tgz", @@ -18004,6 +18023,11 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, + "justified-layout": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/justified-layout/-/justified-layout-4.1.0.tgz", + "integrity": "sha512-M5FimNMXgiOYerVRGsXZ2YK9YNCaTtwtYp7Hb2308U1Q9TXXHx5G0p08mcVR5O53qf8bWY4NJcPBxE6zuayXSg==" + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", diff --git a/web/package.json b/web/package.json index 6bb8c52e82..2a9128542c 100644 --- a/web/package.json +++ b/web/package.json @@ -27,6 +27,7 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/svelte": "^3.2.2", "@types/cookie": "^0.5.1", + "@types/justified-layout": "^4.1.0", "@types/leaflet": "^1.9.1", "@types/lodash-es": "^4.17.6", "@types/luxon": "^3.2.0", @@ -58,6 +59,7 @@ "axios": "^0.27.2", "copy-image-clipboard": "^2.1.2", "handlebars": "^4.7.7", + "justified-layout": "^4.1.0", "leaflet": "^1.9.3", "lodash-es": "^4.17.21", "luxon": "^3.2.1", diff --git a/web/src/lib/components/assets/thumbnail/thumbnail.svelte b/web/src/lib/components/assets/thumbnail/thumbnail.svelte index daf18873b3..c4239f9849 100644 --- a/web/src/lib/components/assets/thumbnail/thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/thumbnail.svelte @@ -15,6 +15,8 @@ export let asset: AssetResponseDto; export let groupIndex = 0; export let thumbnailSize: number | undefined = undefined; + export let thumbnailWidth: number | undefined = undefined; + export let thumbnailHeight: number | undefined = undefined; export let format: ThumbnailFormat = ThumbnailFormat.Webp; export let selected = false; export let disabled = false; @@ -30,6 +32,10 @@ return [thumbnailSize, thumbnailSize]; } + if (thumbnailWidth && thumbnailHeight) { + return [thumbnailWidth, thumbnailHeight]; + } + if (asset.exifInfo?.orientation === 'Rotate 90 CW') { return [176, 235]; } else if (asset.exifInfo?.orientation === 'Horizontal (normal)') { @@ -57,7 +63,9 @@
(mouseOver = true)} diff --git a/web/src/lib/components/photos-page/asset-date-group.svelte b/web/src/lib/components/photos-page/asset-date-group.svelte index 3dd1455d32..f5f0d72d91 100644 --- a/web/src/lib/components/photos-page/asset-date-group.svelte +++ b/web/src/lib/components/photos-page/asset-date-group.svelte @@ -162,7 +162,8 @@ on:click={() => assetClickHandler(asset, assetsInDateGroup, dateGroupTitle)} on:select={() => assetSelectHandler(asset, assetsInDateGroup, dateGroupTitle)} on:mouse-event={() => assetMouseEventHandler(dateGroupTitle)} - selected={$selectedAssets.has(asset)} + selected={$selectedAssets.has(asset) || + $assetsInAlbumStoreState.findIndex((a) => a.id == asset.id) != -1} disabled={$assetsInAlbumStoreState.findIndex((a) => a.id == asset.id) != -1} /> {/each} diff --git a/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte b/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte index e4fe7adc4d..14182c882a 100644 --- a/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte +++ b/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte @@ -3,8 +3,9 @@ import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte'; import { handleError } from '$lib/utils/handle-error'; import { AssetResponseDto, SharedLinkResponseDto, ThumbnailFormat } from '@api'; - import AssetViewer from '../../asset-viewer/asset-viewer.svelte'; + import justifiedLayout from 'justified-layout'; + import { flip } from 'svelte/animate'; export let assets: AssetResponseDto[]; export let sharedLink: SharedLinkResponseDto | undefined = undefined; @@ -17,20 +18,27 @@ let currentViewAssetIndex = 0; let viewWidth: number; - let thumbnailSize = 300; $: isMultiSelectionMode = selectedAssets.size > 0; - $: { - if (assets.length < 6) { - thumbnailSize = Math.min(320, Math.floor(viewWidth / assets.length - assets.length)); - } else { - if (viewWidth > 600) thumbnailSize = Math.floor(viewWidth / 6 - 6); - else if (viewWidth > 400) thumbnailSize = Math.floor(viewWidth / 4 - 6); - else if (viewWidth > 300) thumbnailSize = Math.floor(viewWidth / 2 - 6); - else if (viewWidth > 200) thumbnailSize = Math.floor(viewWidth / 2 - 6); - else if (viewWidth > 100) thumbnailSize = Math.floor(viewWidth / 1 - 6); + function getAssetRatio(asset: AssetResponseDto): number { + const height = asset.exifInfo?.exifImageHeight; + const width = asset.exifInfo?.exifImageWidth; + const orientation = Number(asset.exifInfo?.orientation); + + if (height && width) { + if (orientation) { + if (orientation == 6 || orientation == -90) { + return height / width; + } else { + return width / height; + } + } + + return width / height; } + + return 1; } const viewAssetHandler = (event: CustomEvent) => { @@ -93,18 +101,29 @@ {#if assets.length > 0}
- {#each assets as asset (asset.id)} - (isMultiSelectionMode ? selectAssetHandler(e) : viewAssetHandler(e))} - on:select={selectAssetHandler} - selected={selectedAssets.has(asset)} - /> - {/each} + {#if viewWidth} + {@const geoArray = assets.map(getAssetRatio)} + {@const justifiedLayoutResult = justifiedLayout(geoArray, { + targetRowHeight: 235, + containerWidth: Math.floor(viewWidth) + })} + + {#each assets as asset, index (asset.id)} +
+ (isMultiSelectionMode ? selectAssetHandler(e) : viewAssetHandler(e))} + on:select={selectAssetHandler} + selected={selectedAssets.has(asset)} + /> +
+ {/each} + {/if}
{/if}