소스 검색

feat(web): add zoom toggle icon (#2873)

* feat(web): add zoom toggle icon

* update zoom-image dependency

* fix lint issues

* remove variable testing line

* Simplify code using ternary conditional

Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>

* fix typo

---------

Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>
Manuel Taberna 2 년 전
부모
커밋
48e4ea5231

+ 1 - 0
web/src/app.d.ts

@@ -29,5 +29,6 @@ declare namespace svelteHTML {
 	// eslint-disable-next-line @typescript-eslint/no-unused-vars
 	interface HTMLAttributes<T> {
 		'on:copyImage'?: () => void;
+		'on:zoomImage'?: () => void;
 	}
 }

+ 18 - 0
web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte

@@ -11,14 +11,18 @@
 	import Heart from 'svelte-material-icons/Heart.svelte';
 	import HeartOutline from 'svelte-material-icons/HeartOutline.svelte';
 	import InformationOutline from 'svelte-material-icons/InformationOutline.svelte';
+	import MagnifyPlusOutline from 'svelte-material-icons/MagnifyPlusOutline.svelte';
+	import MagnifyMinusOutline from 'svelte-material-icons/MagnifyMinusOutline.svelte';
 	import MotionPauseOutline from 'svelte-material-icons/MotionPauseOutline.svelte';
 	import MotionPlayOutline from 'svelte-material-icons/MotionPlayOutline.svelte';
 	import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
 	import ContextMenu from '../shared-components/context-menu/context-menu.svelte';
 	import MenuOption from '../shared-components/context-menu/menu-option.svelte';
+	import { photoZoomState } from '$lib/stores/zoom-image.store';
 
 	export let asset: AssetResponseDto;
 	export let showCopyButton: boolean;
+	export let showZoomButton: boolean;
 	export let showMotionPlayButton: boolean;
 	export let isMotionPhotoPlaying = false;
 	export let showDownloadButton: boolean;
@@ -65,6 +69,20 @@
 				/>
 			{/if}
 		{/if}
+		{#if showZoomButton}
+			<CircleIconButton
+				isOpacity={true}
+				hideMobile={true}
+				logo={$photoZoomState && $photoZoomState.currentZoom > 1
+					? MagnifyMinusOutline
+					: MagnifyPlusOutline}
+				title="Zoom Image"
+				on:click={() => {
+					const zoomImage = new CustomEvent('zoomImage');
+					window.dispatchEvent(zoomImage);
+				}}
+			/>
+		{/if}
 		{#if showCopyButton}
 			<CircleIconButton
 				isOpacity={true}

+ 1 - 0
web/src/lib/components/asset-viewer/asset-viewer.svelte

@@ -308,6 +308,7 @@
 			{asset}
 			isMotionPhotoPlaying={shouldPlayMotionPhoto}
 			showCopyButton={canCopyImagesToClipboard && asset.type === AssetTypeEnum.Image}
+			showZoomButton={asset.type === AssetTypeEnum.Image}
 			showMotionPlayButton={!!asset.livePhotoVideoId}
 			showDownloadButton={shouldShowDownloadButton}
 			on:goBack={closeViewer}

+ 18 - 2
web/src/lib/components/asset-viewer/photo-viewer.svelte

@@ -8,6 +8,7 @@
 		NotificationType
 	} from '../shared-components/notification/notification';
 	import { useZoomImageWheel } from '@zoom-image/svelte';
+	import { photoZoomState } from '$lib/stores/zoom-image.store';
 
 	export let asset: AssetResponseDto;
 	export let publicSharedKey = '';
@@ -73,7 +74,22 @@
 		}
 	};
 
-	const { createZoomImage: createZoomImageWheel } = useZoomImageWheel();
+	const doZoomImage = async () => {
+		setZoomImageWheelState({
+			currentZoom: $zoomImageWheelState.currentZoom === 1 ? 2 : 1
+		});
+	};
+
+	const {
+		createZoomImage: createZoomImageWheel,
+		zoomImageState: zoomImageWheelState,
+		setZoomImageState: setZoomImageWheelState
+	} = useZoomImageWheel();
+
+	zoomImageWheelState.subscribe((state) => {
+		photoZoomState.set(state);
+	});
+
 	$: if (imgElement) {
 		createZoomImageWheel(imgElement, {
 			wheelZoomRatio: 0.06
@@ -81,7 +97,7 @@
 	}
 </script>
 
-<svelte:window on:keydown={handleKeypress} on:copyImage={doCopy} />
+<svelte:window on:keydown={handleKeypress} on:copyImage={doCopy} on:zoomImage={doZoomImage} />
 
 <div
 	transition:fade={{ duration: 150 }}

+ 3 - 1
web/src/lib/components/elements/buttons/circle-icon-button.svelte

@@ -8,6 +8,7 @@
 	export let title = '';
 	export let isOpacity = false;
 	export let forceDark = false;
+	export let hideMobile = false;
 </script>
 
 <button
@@ -17,7 +18,8 @@
 	class:dark:text-immich-dark-fg={!forceDark}
 	class="rounded-full p-3 flex place-items-center place-content-center transition-all
 	{isOpacity ? 'hover:bg-immich-bg/30' : 'immich-circle-icon-button hover:dark:text-immich-dark-gray'}
-  {forceDark && 'hover:text-black'}"
+  {forceDark && 'hover:text-black'}
+  {hideMobile && 'hidden sm:flex'}"
 	on:click
 >
 	<svelte:component this={logo} {size} />

+ 4 - 0
web/src/lib/stores/zoom-image.store.ts

@@ -0,0 +1,4 @@
+import { writable } from 'svelte/store';
+import type { ZoomImageWheelState } from '@zoom-image/core';
+
+export const photoZoomState = writable<ZoomImageWheelState>();