Convert zoom controls into hook

This commit is contained in:
Sam Becker 2025-01-26 10:24:00 -06:00
parent 5139abcdba
commit 2195379b74
6 changed files with 58 additions and 77 deletions

View File

@ -23,6 +23,7 @@ import ShareModals from '@/share/ShareModals';
import '../site/globals.css'; import '../site/globals.css';
import '../site/sonner.css'; import '../site/sonner.css';
import '../site/viewerjs.css';
const ibmPlexMono = IBM_Plex_Mono({ const ibmPlexMono = IBM_Plex_Mono({
subsets: ['latin'], subsets: ['latin'],

View File

@ -1,68 +0,0 @@
import { useEffect, useRef } from 'react';
import Viewer from 'viewerjs';
import 'viewerjs/dist/viewer.css';
import { clsx } from 'clsx/lite';
import FullscreenButton from '../FullscreenButton';
export default function ImageZoomControls({
children,
enableImageActions = false,
className,
}: {
children: React.ReactNode;
enableImageActions?: boolean;
className?: string;
}) {
const containerRef = useRef<HTMLDivElement>(null);
const viewerRef = useRef<Viewer | null>(null);
useEffect(() => {
if (containerRef.current && enableImageActions) {
viewerRef.current = new Viewer(containerRef.current, {
inline: false,
button: true,
navbar: false,
title: false,
toolbar: {
zoomIn: 1,
zoomOut: 1,
reset: 1,
tooltip: 1,
},
});
return () => {
viewerRef.current?.destroy();
};
}
}, [enableImageActions]);
return (
<>
<style jsx global>{`
.viewer-canvas {
background-color: black !important;
}
.viewer-reset::before {
content: '1:1';
font-size: 13px;
font-weight: 600;
color: #fff;
display: inline-block;
position: relative;
bottom: -9px;
letter-spacing: -2px;
background-image: none;
}
`}</style>
<div
className={clsx(className, enableImageActions && 'cursor-zoom-in')}
ref={containerRef}
>
{children}
{enableImageActions && (
<FullscreenButton imageRef={containerRef} />
)}
</div>
</>
);
}

View File

@ -0,0 +1,29 @@
import { RefObject, useEffect, useRef } from 'react';
import Viewer from 'viewerjs';
export default function useImageZoomControls(
imageRef: RefObject<HTMLDivElement | null>,
isEnabled?: boolean,
) {
const viewerRef = useRef<Viewer | null>(null);
useEffect(() => {
if (imageRef.current && isEnabled) {
viewerRef.current = new Viewer(imageRef.current, {
inline: false,
button: true,
navbar: false,
title: false,
toolbar: {
zoomIn: 1,
zoomOut: 1,
reset: 1,
tooltip: 1,
},
});
return () => {
viewerRef.current?.destroy();
};
}
}, [imageRef, isEnabled]);
}

View File

@ -113,7 +113,7 @@ export default function PhotoDetailPage({
shouldShareSimulation={simulation !== undefined} shouldShareSimulation={simulation !== undefined}
shouldScrollOnShare={false} shouldScrollOnShare={false}
includeFavoriteInAdminMenu={includeFavoriteInAdminMenu} includeFavoriteInAdminMenu={includeFavoriteInAdminMenu}
enableImageActions={ZOOM_CONTROLS_ENABLED} shouldShowZoomControls={ZOOM_CONTROLS_ENABLED}
/>, />,
]} ]}
/> />

View File

@ -36,7 +36,7 @@ import { useRef } from 'react';
import useOnVisible from '@/utility/useOnVisible'; import useOnVisible from '@/utility/useOnVisible';
import PhotoDate from './PhotoDate'; import PhotoDate from './PhotoDate';
import { useAppState } from '@/state/AppState'; import { useAppState } from '@/state/AppState';
import ImageZoomControls from '@/components/image/ImageZoomControls'; import useImageZoomControls from '@/components/image/useImageZoomControls';
export default function PhotoLarge({ export default function PhotoLarge({
photo, photo,
@ -55,9 +55,9 @@ export default function PhotoLarge({
shouldShareCamera, shouldShareCamera,
shouldShareSimulation, shouldShareSimulation,
shouldShareFocalLength, shouldShareFocalLength,
shouldShowZoomControls,
includeFavoriteInAdminMenu, includeFavoriteInAdminMenu,
onVisible, onVisible,
enableImageActions = false,
}: { }: {
photo: Photo photo: Photo
className?: string className?: string
@ -76,11 +76,12 @@ export default function PhotoLarge({
shouldShareSimulation?: boolean shouldShareSimulation?: boolean
shouldShareFocalLength?: boolean shouldShareFocalLength?: boolean
shouldScrollOnShare?: boolean shouldScrollOnShare?: boolean
shouldShowZoomControls?: boolean
includeFavoriteInAdminMenu?: boolean includeFavoriteInAdminMenu?: boolean
onVisible?: () => void onVisible?: () => void
enableImageActions?: boolean
}) { }) {
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const refZoomControls = useRef<HTMLDivElement>(null);
const tags = sortTags(photo.tags, primaryTag); const tags = sortTags(photo.tags, primaryTag);
@ -92,6 +93,8 @@ export default function PhotoLarge({
useOnVisible(ref, onVisible); useOnVisible(ref, onVisible);
useImageZoomControls(refZoomControls, shouldShowZoomControls);
const { arePhotosMatted, isUserSignedIn } = useAppState(); const { arePhotosMatted, isUserSignedIn } = useAppState();
const hasTitle = const hasTitle =
@ -146,9 +149,9 @@ export default function PhotoLarge({
arePhotosMatted && 'h-[90%]', arePhotosMatted && 'h-[90%]',
arePhotosMatted && matteContentWidthForAspectRatio(), arePhotosMatted && matteContentWidthForAspectRatio(),
)}> )}>
<ImageZoomControls <div
enableImageActions={enableImageActions} ref={refZoomControls}
className="flex relative items-center justify-center h-full" className={clsx(shouldShowZoomControls && 'cursor-zoom-in')}
> >
<ImageLarge <ImageLarge
className={clsx(arePhotosMatted && 'h-full')} className={clsx(arePhotosMatted && 'h-full')}
@ -161,7 +164,7 @@ export default function PhotoLarge({
blurCompatibilityMode={doesPhotoNeedBlurCompatibility(photo)} blurCompatibilityMode={doesPhotoNeedBlurCompatibility(photo)}
priority={priority} priority={priority}
/> />
</ImageZoomControls> </div>
</div> </div>
</Link>} </Link>}
contentSide={ contentSide={

16
src/site/viewerjs.css Normal file
View File

@ -0,0 +1,16 @@
@import 'viewerjs/dist/viewer.css';
.viewer-canvas {
background-color: black !important;
}
.viewer-reset::before {
content: '1:1';
font-size: 13px;
font-weight: 600;
color: #fff;
display: inline-block;
position: relative;
bottom: -9px;
letter-spacing: -2px;
background-image: none;
}