diff --git a/ui/v2.5/package.json b/ui/v2.5/package.json index f52844a90..96538c877 100644 --- a/ui/v2.5/package.json +++ b/ui/v2.5/package.json @@ -52,6 +52,7 @@ "react-dom": "^17.0.2", "react-helmet": "^6.1.0", "react-intl": "^6.2.8", + "react-photo-gallery": "^8.0.0", "react-remark": "^2.1.0", "react-router-bootstrap": "^0.25.0", "react-router-dom": "^5.3.4", diff --git a/ui/v2.5/src/components/Galleries/GalleryViewer.tsx b/ui/v2.5/src/components/Galleries/GalleryViewer.tsx index 67736c50d..5eb9deae6 100644 --- a/ui/v2.5/src/components/Galleries/GalleryViewer.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryViewer.tsx @@ -1,6 +1,7 @@ -import React, { useMemo } from "react"; +import React, { useCallback, useMemo } from "react"; import { useLightbox } from "src/hooks/Lightbox/hooks"; import { LoadingIndicator } from "src/components/Shared/LoadingIndicator"; +import Gallery from "react-photo-gallery"; import "flexbin/flexbin.css"; import { CriterionModifier, @@ -44,29 +45,42 @@ export const GalleryViewer: React.FC = ({ galleryId }) => { }, [images]); const showLightbox = useLightbox(lightboxState); + const showLightboxOnClick = useCallback( + (event, { index }) => { + showLightbox(index); + }, + [showLightbox] + ); if (loading) return ; - const thumbs = images.map((file, index) => ( -
showLightbox(index)} - onKeyPress={() => showLightbox(index)} - > - {file.title -
- )); + let photos: { + src: string; + srcSet?: string | string[] | undefined; + sizes?: string | string[] | undefined; + width: number; + height: number; + alt?: string | undefined; + key?: string | undefined; + }[] = []; + + images.forEach((image, index) => { + let imageData = { + src: image.paths.thumbnail!, + width: image.files[0].width, + height: image.files[0].height, + tabIndex: index, + key: image.id ?? index, + loading: "lazy", + className: "gallery-image", + alt: image.title ?? index.toString(), + }; + photos.push(imageData); + }); return (
-
{thumbs}
+
); }; diff --git a/ui/v2.5/src/components/Images/ImageList.tsx b/ui/v2.5/src/components/Images/ImageList.tsx index 19223bb02..b3ce6b0c3 100644 --- a/ui/v2.5/src/components/Images/ImageList.tsx +++ b/ui/v2.5/src/components/Images/ImageList.tsx @@ -1,4 +1,10 @@ -import React, { useCallback, useState, useMemo, MouseEvent } from "react"; +import React, { + useCallback, + useState, + useMemo, + MouseEvent, + useContext, +} from "react"; import { FormattedNumber, useIntl } from "react-intl"; import cloneDeep from "lodash-es/cloneDeep"; import { useHistory } from "react-router-dom"; @@ -19,9 +25,12 @@ import { ImageCard } from "./ImageCard"; import { EditImagesDialog } from "./EditImagesDialog"; import { DeleteImagesDialog } from "./DeleteImagesDialog"; import "flexbin/flexbin.css"; +import Gallery from "react-photo-gallery"; import { ExportDialog } from "../Shared/ExportDialog"; import { objectTitle } from "src/core/files"; import TextUtils from "src/utils/text"; +import { ConfigurationContext } from "src/hooks/Config"; +import { IUIConfig } from "src/core/config"; interface IImageWallProps { images: GQL.SlimImageDataFragment[]; @@ -32,26 +41,55 @@ interface IImageWallProps { } const ImageWall: React.FC = ({ images, handleImageOpen }) => { - const thumbs = images.map((image, index) => ( -
handleImageOpen(index)} - onKeyPress={() => handleImageOpen(index)} - > - {objectTitle(image)} -
- )); + const { configuration } = useContext(ConfigurationContext); + const uiConfig = configuration?.ui as IUIConfig | undefined; + + let photos: { + src: string; + srcSet?: string | string[] | undefined; + sizes?: string | string[] | undefined; + width: number; + height: number; + alt?: string | undefined; + key?: string | undefined; + }[] = []; + + images.forEach((image, index) => { + let imageData = { + src: image.paths.thumbnail!, + width: image.files[0].width, + height: image.files[0].height, + tabIndex: index, + key: image.id, + loading: "lazy", + className: "gallery-image", + alt: objectTitle(image), + }; + photos.push(imageData); + }); + + const showLightboxOnClick = useCallback( + (event, { index }) => { + handleImageOpen(index); + }, + [handleImageOpen] + ); + + function columns(containerWidth: number) { + let preferredSize = 250; + let columnCount = containerWidth / preferredSize; + return Math.floor(columnCount); + } return (
-
{thumbs}
+
); }; diff --git a/ui/v2.5/src/components/Settings/SettingsInterfacePanel/SettingsInterfacePanel.tsx b/ui/v2.5/src/components/Settings/SettingsInterfacePanel/SettingsInterfacePanel.tsx index 50576baae..97866b4d4 100644 --- a/ui/v2.5/src/components/Settings/SettingsInterfacePanel/SettingsInterfacePanel.tsx +++ b/ui/v2.5/src/components/Settings/SettingsInterfacePanel/SettingsInterfacePanel.tsx @@ -35,6 +35,13 @@ import { ratingSystemIntlMap, RatingSystemType, } from "src/utils/rating"; +import { + imageWallDirectionIntlMap, + ImageWallDirection, + defaultImageWallOptions, + defaultImageWallDirection, + defaultImageWallMargin, +} from "src/utils/imageWall"; import { defaultMaxOptionsShown } from "src/core/config"; const allMenuItems = [ @@ -92,6 +99,24 @@ export const SettingsInterfacePanel: React.FC = () => { }); } + function saveImageWallMargin(m: number) { + saveUI({ + imageWallOptions: { + ...(ui.imageWallOptions ?? defaultImageWallOptions), + margin: m, + }, + }); + } + + function saveImageWallDirection(d: ImageWallDirection) { + saveUI({ + imageWallOptions: { + ...(ui.imageWallOptions ?? defaultImageWallOptions), + direction: d, + }, + }); + } + function saveRatingSystemType(t: RatingSystemType) { saveUI({ ratingSystemOptions: { @@ -353,6 +378,31 @@ export const SettingsInterfacePanel: React.FC = () => { /> + + saveImageWallMargin(v)} + /> + + saveImageWallDirection(v as ImageWallDirection)} + > + {Array.from(imageWallDirectionIntlMap.entries()).map((v) => ( + + ))} + + + ([ + [ImageWallDirection.Column, "dialogs.imagewall.direction.column"], + [ImageWallDirection.Row, "dialogs.imagewall.direction.row"], +]); + +export const defaultImageWallOptions = { + margin: defaultImageWallMargin, + direction: defaultImageWallDirection, +}; diff --git a/ui/v2.5/yarn.lock b/ui/v2.5/yarn.lock index 16fae3619..21d2aad92 100644 --- a/ui/v2.5/yarn.lock +++ b/ui/v2.5/yarn.lock @@ -6368,6 +6368,15 @@ prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, object-assign "^4.1.1" react-is "^16.13.1" +prop-types@~15.7.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + property-expr@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.5.tgz#278bdb15308ae16af3e3b9640024524f4dc02cb4" @@ -6485,7 +6494,7 @@ react-intl@^6.2.8: intl-messageformat "10.3.0" tslib "^2.4.0" -react-is@^16.13.1, react-is@^16.3.2, react-is@^16.6.0, react-is@^16.7.0: +react-is@^16.13.1, react-is@^16.3.2, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -6509,6 +6518,14 @@ react-overlays@^5.1.2: uncontrollable "^7.2.1" warning "^4.0.3" +react-photo-gallery@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/react-photo-gallery/-/react-photo-gallery-8.0.0.tgz#04ff9f902a2342660e63e6817b4f010488db02b8" + integrity sha512-Y9458yygEB9cIZAWlBWuenlR+ghin1RopmmU3Vice8BeJl0Se7hzfxGDq8W1armB/ic/kphGg+G1jq5fOEd0sw== + dependencies: + prop-types "~15.7.2" + resize-observer-polyfill "^1.5.0" + react-refresh@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" @@ -6786,7 +6803,7 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -resize-observer-polyfill@^1.5.1: +resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==