From 8c69edd01f402f786cfefbde1fc77e7c5592a9b9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?=
 <60678893+juliajforesti@users.noreply.github.com>
Date: Wed, 10 Jan 2024 14:02:04 -0300
Subject: [PATCH] feat: add `ImageGallery` zoom controls (#31369)

---
 .changeset/funny-buses-own.md                 |  5 ++
 .../components/ImageGallery/ImageGallery.tsx  | 57 ++++++++++++++-----
 2 files changed, 49 insertions(+), 13 deletions(-)
 create mode 100644 .changeset/funny-buses-own.md

diff --git a/.changeset/funny-buses-own.md b/.changeset/funny-buses-own.md
new file mode 100644
index 00000000000..faa0159807c
--- /dev/null
+++ b/.changeset/funny-buses-own.md
@@ -0,0 +1,5 @@
+---
+'@rocket.chat/meteor': minor
+---
+
+feat: add `ImageGallery` zoom controls
diff --git a/apps/meteor/client/components/ImageGallery/ImageGallery.tsx b/apps/meteor/client/components/ImageGallery/ImageGallery.tsx
index ebce7d42e54..676418731e5 100644
--- a/apps/meteor/client/components/ImageGallery/ImageGallery.tsx
+++ b/apps/meteor/client/components/ImageGallery/ImageGallery.tsx
@@ -1,5 +1,5 @@
 import { css } from '@rocket.chat/css-in-js';
-import { Box, IconButton, Palette, Throbber } from '@rocket.chat/fuselage';
+import { Box, ButtonGroup, IconButton, Palette, Throbber } from '@rocket.chat/fuselage';
 import React, { useRef, useState } from 'react';
 import { FocusScope } from 'react-aria';
 import { createPortal } from 'react-dom';
@@ -13,6 +13,7 @@ import 'swiper/modules/navigation/navigation.min.css';
 import 'swiper/modules/keyboard/keyboard.min.css';
 import 'swiper/modules/zoom/zoom.min.css';
 
+import { usePreventPropagation } from '../../hooks/usePreventPropagation';
 import ImageGalleryLoader from './ImageGalleryLoader';
 import { useImageGallery } from './hooks/useImageGallery';
 
@@ -40,13 +41,6 @@ const swiperStyle = css`
 		color: var(--rcx-color-font-pure-white, #ffffff) !important;
 	}
 
-	.rcx-swiper-close-button {
-		position: absolute;
-		z-index: 10;
-		top: 10px;
-		right: 10px;
-	}
-
 	.rcx-swiper-prev-button,
 	.rcx-swiper-next-button {
 		position: absolute;
@@ -94,11 +88,43 @@ const swiperStyle = css`
 
 		color: ${Palette.text['font-pure-white']};
 	}
+
+	.rcx-swiper-controls {
+		position: absolute;
+		top: 0;
+		right: 0;
+		padding: 10px;
+		z-index: 2;
+
+		width: 100%;
+		display: flex;
+		justify-content: flex-end;
+		transition: background-color 0.2s;
+		&:hover {
+			background-color: ${Palette.surface['surface-overlay']};
+			transition: background-color 0.2s;
+		}
+	}
 `;
 
 const ImageGallery = () => {
 	const swiperRef = useRef<SwiperRef>(null);
 	const [, setSwiperInst] = useState<SwiperClass>();
+	const [zoomScale, setZoomScale] = useState(1);
+
+	const handleZoom = (ratio: number) => {
+		if (swiperRef.current?.swiper.zoom) {
+			const { scale, in: zoomIn } = swiperRef.current?.swiper.zoom;
+			setZoomScale(scale + ratio);
+			return zoomIn(scale + ratio);
+		}
+	};
+
+	const handleZoomIn = () => handleZoom(1);
+	const handleZoomOut = () => handleZoom(-1);
+	const handleResize = () => handleZoom(-(zoomScale - 1));
+
+	const preventPropagation = usePreventPropagation();
 
 	const { isLoading, loadMore, images, onClose } = useImageGallery();
 
@@ -110,9 +136,14 @@ const ImageGallery = () => {
 		<FocusScope contain restoreFocus autoFocus>
 			<Box className={swiperStyle}>
 				<div className='swiper-container' onClick={onClose}>
-					<IconButton icon='cross' aria-label='Close gallery' className='rcx-swiper-close-button' onClick={onClose} />
-					<IconButton icon='chevron-right' className='rcx-swiper-prev-button' onClick={(e) => e.stopPropagation()} />
-					<IconButton icon='chevron-left' className='rcx-swiper-next-button' onClick={(e) => e.stopPropagation()} />
+					<ButtonGroup className='rcx-swiper-controls' onClick={preventPropagation}>
+						{zoomScale !== 1 && <IconButton icon='arrow-collapse' title='Resize' rcx-swiper-zoom-out onClick={handleResize} />}
+						<IconButton small icon='h-bar' title='Zoom out' rcx-swiper-zoom-out onClick={handleZoomOut} disabled={zoomScale === 1} />
+						<IconButton small icon='plus' title='Zoom in' rcx-swiper-zoom-in onClick={handleZoomIn} />
+						<IconButton small icon='cross' title='Close' aria-label='Close gallery' className='rcx-swiper-close-button' onClick={onClose} />
+					</ButtonGroup>
+					<IconButton icon='chevron-right' className='rcx-swiper-prev-button' onClick={preventPropagation} />
+					<IconButton icon='chevron-left' className='rcx-swiper-next-button' onClick={preventPropagation} />
 					<Swiper
 						ref={swiperRef}
 						navigation={{
@@ -120,7 +151,7 @@ const ImageGallery = () => {
 							prevEl: '.rcx-swiper-prev-button',
 						}}
 						keyboard
-						zoom
+						zoom={{ toggle: false }}
 						lazyPreloaderClass='rcx-lazy-preloader'
 						runCallbacksOnInit
 						onKeyPress={(_, keyCode) => String(keyCode) === '27' && onClose()}
@@ -131,7 +162,7 @@ const ImageGallery = () => {
 						{images?.map(({ _id, url }) => (
 							<SwiperSlide key={_id}>
 								<div className='swiper-zoom-container'>
-									<img src={url} loading='lazy' onClick={(e) => e.stopPropagation()} />
+									<img src={url} loading='lazy' onClick={preventPropagation} />
 									<div className='rcx-lazy-preloader'>
 										<Throbber inheritColor />
 									</div>
-- 
GitLab