Add wall zoom functionality (#6011)

* Show zoom slider when wall view active
* Add zoom functionality to scene wall
* Add zoom functionality to image wall
* Add zoom functionality to gallery wall
* Add zoom functionality for marker wall
This commit is contained in:
WithoutPants 2025-09-09 16:45:29 +10:00 committed by GitHub
parent b1883f3df5
commit cc97e96eaa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 148 additions and 37 deletions

View file

@ -149,7 +149,7 @@ export const GalleryList: React.FC<IGalleryList> = ({
if (filter.displayMode === DisplayMode.Wall) {
return (
<div className="row">
<div className="GalleryWall">
<div className={`GalleryWall zoom-${filter.zoomIndex}`}>
{result.data.findGalleries.galleries.map((gallery) => (
<GalleryWallCard key={gallery.id} gallery={gallery} />
))}

View file

@ -206,7 +206,7 @@ $galleryTabWidth: 450px;
}
}
.GalleryWall {
div.GalleryWall {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
@ -249,28 +249,6 @@ $galleryTabWidth: 450px;
z-index: 1;
}
@mixin galleryWidth($width) {
height: math.div($width, 3) * 2;
&-landscape {
width: $width;
}
&-portrait {
width: math.div($width, 2);
}
}
@media (min-width: 576px) {
@include galleryWidth(96vw);
}
@media (min-width: 768px) {
@include galleryWidth(48vw);
}
@media (min-width: 1200px) {
@include galleryWidth(32vw);
}
&-img {
height: 100%;
object-fit: cover;
@ -355,6 +333,62 @@ $galleryTabWidth: 450px;
}
}
div.GalleryWall {
@mixin galleryWidth($width) {
height: math.div($width, 3) * 2;
&-landscape {
width: $width;
}
&-portrait {
width: math.div($width, 2);
}
}
.GalleryWallCard {
@media (min-width: 576px) {
@include galleryWidth(96vw);
}
}
&.zoom-0 .GalleryWallCard {
@media (min-width: 768px) {
@include galleryWidth(16vw);
}
@media (min-width: 1200px) {
@include galleryWidth(10vw);
}
}
&.zoom-1 .GalleryWallCard {
@media (min-width: 768px) {
@include galleryWidth(24vw);
}
@media (min-width: 1200px) {
@include galleryWidth(16vw);
}
}
&.zoom-2 .GalleryWallCard {
@media (min-width: 768px) {
@include galleryWidth(32vw);
}
@media (min-width: 1200px) {
@include galleryWidth(24vw);
}
}
&.zoom-3 .GalleryWallCard {
@media (min-width: 768px) {
@include galleryWidth(48vw);
}
@media (min-width: 1200px) {
@include galleryWidth(32vw);
}
}
}
.gallery-file-card.card {
margin: 0;
padding: 0;

View file

@ -35,9 +35,22 @@ interface IImageWallProps {
currentPage: number;
pageCount: number;
handleImageOpen: (index: number) => void;
zoomIndex: number;
}
const ImageWall: React.FC<IImageWallProps> = ({ images, handleImageOpen }) => {
const zoomWidths = [280, 340, 480, 640];
const breakpointZoomHeights = [
{ minWidth: 576, heights: [100, 120, 240, 360] },
{ minWidth: 768, heights: [120, 160, 240, 480] },
{ minWidth: 1200, heights: [120, 160, 240, 300] },
{ minWidth: 1400, heights: [160, 240, 300, 480] },
];
const ImageWall: React.FC<IImageWallProps> = ({
images,
zoomIndex,
handleImageOpen,
}) => {
const { configuration } = useContext(ConfigurationContext);
const uiConfig = configuration?.ui;
@ -76,11 +89,21 @@ const ImageWall: React.FC<IImageWallProps> = ({ images, handleImageOpen }) => {
);
function columns(containerWidth: number) {
let preferredSize = 300;
let preferredSize = zoomWidths[zoomIndex];
let columnCount = containerWidth / preferredSize;
return Math.round(columnCount);
}
function targetRowHeight(containerWidth: number) {
let zoomHeight = 280;
breakpointZoomHeights.forEach((e) => {
if (containerWidth >= e.minWidth) {
zoomHeight = e.heights[zoomIndex];
}
});
return zoomHeight;
}
return (
<div className="gallery">
{photos.length ? (
@ -91,6 +114,7 @@ const ImageWall: React.FC<IImageWallProps> = ({ images, handleImageOpen }) => {
margin={uiConfig?.imageWallOptions?.margin!}
direction={uiConfig?.imageWallOptions?.direction!}
columns={columns}
targetRowHeight={targetRowHeight}
/>
) : null}
</div>
@ -211,6 +235,7 @@ const ImageListImages: React.FC<IImageListImages> = ({
currentPage={filter.currentPage}
pageCount={pageCount}
handleImageOpen={handleImageOpen}
zoomIndex={filter.zoomIndex}
/>
);
}

View file

@ -130,7 +130,8 @@ export const ListViewOptions: React.FC<IListViewOptionsProps> = ({
<div className="display-mode-menu">
{onSetZoom &&
zoomIndex !== undefined &&
displayMode === DisplayMode.Grid ? (
(displayMode === DisplayMode.Grid ||
displayMode === DisplayMode.Wall) ? (
<div className="zoom-slider-container">
<ZoomSelect
minZoom={minZoom}

View file

@ -219,7 +219,13 @@ const SceneList: React.FC<{
);
}
if (filter.displayMode === DisplayMode.Wall) {
return <SceneWallPanel scenes={scenes} sceneQueue={queue} />;
return (
<SceneWallPanel
scenes={scenes}
sceneQueue={queue}
zoomIndex={filter.zoomIndex}
/>
);
}
if (filter.displayMode === DisplayMode.Tagger) {
return <Tagger scenes={scenes} queue={queue} />;

View file

@ -95,7 +95,10 @@ export const SceneMarkerList: React.FC<ISceneMarkerList> = ({
if (filter.displayMode === DisplayMode.Wall) {
return (
<MarkerWallPanel markers={result.data.findSceneMarkers.scene_markers} />
<MarkerWallPanel
markers={result.data.findSceneMarkers.scene_markers}
zoomIndex={filter.zoomIndex}
/>
);
}

View file

@ -120,6 +120,7 @@ export const MarkerWallItem: React.FC<RenderImageProps<IMarkerPhoto>> = (
interface IMarkerWallProps {
markers: GQL.SceneMarkerDataFragment[];
zoomIndex: number;
}
// HACK: typescript doesn't allow Gallery to accept a parameter for some reason
@ -152,9 +153,14 @@ function getDimensions(file?: IFile) {
};
}
const defaultTargetRowHeight = 250;
const breakpointZoomHeights = [
{ minWidth: 576, heights: [100, 120, 240, 360] },
{ minWidth: 768, heights: [120, 160, 240, 480] },
{ minWidth: 1200, heights: [120, 160, 240, 300] },
{ minWidth: 1400, heights: [160, 240, 300, 480] },
];
const MarkerWall: React.FC<IMarkerWallProps> = ({ markers }) => {
const MarkerWall: React.FC<IMarkerWallProps> = ({ markers, zoomIndex }) => {
const history = useHistory();
const margin = 3;
@ -202,6 +208,16 @@ const MarkerWall: React.FC<IMarkerWallProps> = ({ markers }) => {
return Math.round(columnCount);
}
function targetRowHeight(containerWidth: number) {
let zoomHeight = 280;
breakpointZoomHeights.forEach((e) => {
if (containerWidth >= e.minWidth) {
zoomHeight = e.heights[zoomIndex];
}
});
return zoomHeight;
}
const renderImage = useCallback((props: RenderImageProps<IMarkerPhoto>) => {
return <MarkerWallItem {...props} />;
}, []);
@ -216,7 +232,7 @@ const MarkerWall: React.FC<IMarkerWallProps> = ({ markers }) => {
margin={margin}
direction={direction}
columns={columns}
targetRowHeight={defaultTargetRowHeight}
targetRowHeight={targetRowHeight}
/>
) : null}
</div>
@ -225,10 +241,12 @@ const MarkerWall: React.FC<IMarkerWallProps> = ({ markers }) => {
interface IMarkerWallPanelProps {
markers: GQL.SceneMarkerDataFragment[];
zoomIndex: number;
}
export const MarkerWallPanel: React.FC<IMarkerWallPanelProps> = ({
markers,
zoomIndex,
}) => {
return <MarkerWall markers={markers} />;
return <MarkerWall markers={markers} zoomIndex={zoomIndex} />;
};

View file

@ -126,14 +126,24 @@ function getDimensions(s: GQL.SlimSceneDataFragment) {
interface ISceneWallProps {
scenes: GQL.SlimSceneDataFragment[];
sceneQueue?: SceneQueue;
zoomIndex: number;
}
// HACK: typescript doesn't allow Gallery to accept a parameter for some reason
const SceneGallery = Gallery as unknown as GalleryI<IScenePhoto>;
const defaultTargetRowHeight = 250;
const breakpointZoomHeights = [
{ minWidth: 576, heights: [100, 120, 240, 360] },
{ minWidth: 768, heights: [120, 160, 240, 480] },
{ minWidth: 1200, heights: [120, 160, 240, 300] },
{ minWidth: 1400, heights: [160, 240, 300, 480] },
];
const SceneWall: React.FC<ISceneWallProps> = ({ scenes, sceneQueue }) => {
const SceneWall: React.FC<ISceneWallProps> = ({
scenes,
sceneQueue,
zoomIndex,
}) => {
const history = useHistory();
const margin = 3;
@ -186,6 +196,16 @@ const SceneWall: React.FC<ISceneWallProps> = ({ scenes, sceneQueue }) => {
return Math.round(columnCount);
}
function targetRowHeight(containerWidth: number) {
let zoomHeight = 280;
breakpointZoomHeights.forEach((e) => {
if (containerWidth >= e.minWidth) {
zoomHeight = e.heights[zoomIndex];
}
});
return zoomHeight;
}
const renderImage = useCallback((props: RenderImageProps<IScenePhoto>) => {
return <SceneWallItem {...props} />;
}, []);
@ -200,7 +220,7 @@ const SceneWall: React.FC<ISceneWallProps> = ({ scenes, sceneQueue }) => {
margin={margin}
direction={direction}
columns={columns}
targetRowHeight={defaultTargetRowHeight}
targetRowHeight={targetRowHeight}
/>
) : null}
</div>
@ -210,11 +230,15 @@ const SceneWall: React.FC<ISceneWallProps> = ({ scenes, sceneQueue }) => {
interface ISceneWallPanelProps {
scenes: GQL.SlimSceneDataFragment[];
sceneQueue?: SceneQueue;
zoomIndex: number;
}
export const SceneWallPanel: React.FC<ISceneWallPanelProps> = ({
scenes,
sceneQueue,
zoomIndex,
}) => {
return <SceneWall scenes={scenes} sceneQueue={sceneQueue} />;
return (
<SceneWall scenes={scenes} sceneQueue={sceneQueue} zoomIndex={zoomIndex} />
);
};