Refactor viewerjs code into separate ImageActions.tsx component and revert ImageWithFallback.tsx

This commit is contained in:
carlobortolan 2025-01-25 21:29:51 +01:00
parent cd7b49042b
commit 038f6bc26c
No known key found for this signature in database
GPG Key ID: 574D9F10F0EED1BE
4 changed files with 108 additions and 98 deletions

View File

@ -11,7 +11,7 @@ export default function FullscreenButton({
imageRef,
}: {
className?: string;
imageRef: RefObject<HTMLImageElement | null>;
imageRef: RefObject<HTMLDivElement | null>;
}) {
const { isFullscreen, setIsFullscreen } = useAppState();
@ -55,7 +55,7 @@ export default function FullscreenButton({
title="Toggle Fullscreen"
className={clsx(
className,
'text-medium absolute bottom-2 right-2 bg-white p-2 rounded',
'text-medium absolute bottom-2 right-2 bg-white p-2 rounded hidden md:block',
)}
icon={isFullscreen ? <MdFullscreenExit size={18} />
: <MdFullscreen size={18} />}

View File

@ -0,0 +1,55 @@
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 ImageActions({ 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

@ -1,18 +1,22 @@
import { IMAGE_WIDTH_LARGE, ImageProps } from '.';
import ImageWithFallback from './ImageWithFallback';
import ImageActions from './ImageActions';
export default function ImageLarge(props: ImageProps) {
const {
aspectRatio,
blurCompatibilityMode,
enableImageActions = false,
...rest
} = props;
return (
<ImageWithFallback {...{
...rest,
blurCompatibilityLevel: blurCompatibilityMode ? 'high' : 'none',
width: IMAGE_WIDTH_LARGE,
height: Math.round(IMAGE_WIDTH_LARGE / aspectRatio),
}} />
<ImageActions enableImageActions={enableImageActions} className="flex relative items-center justify-center h-full">
<ImageWithFallback {...{
...rest,
blurCompatibilityLevel: blurCompatibilityMode ? 'high' : 'none',
width: IMAGE_WIDTH_LARGE,
height: Math.round(IMAGE_WIDTH_LARGE / aspectRatio),
}} />
</ImageActions>
);
};

View File

@ -3,18 +3,13 @@
/* eslint-disable jsx-a11y/alt-text */
import { BLUR_ENABLED } from '@/site/config';
import { useAppState } from '@/state/AppState';
import { clsx } from 'clsx/lite';
import { clsx} from 'clsx/lite';
import Image, { ImageProps } from 'next/image';
import { useCallback, useEffect, useRef, useState } from 'react';
import FullscreenButton from '../FullscreenButton';
import Viewer from 'viewerjs';
import 'viewerjs/dist/viewer.css';
export default function ImageWithFallback(props: ImageProps & {
blurCompatibilityLevel?: 'none' | 'low' | 'high'
imgClassName?: string
allowFullscreen?: boolean
enableImageActions?: boolean
}) {
const {
className,
@ -22,7 +17,6 @@ export default function ImageWithFallback(props: ImageProps & {
blurDataURL,
blurCompatibilityLevel = 'low',
imgClassName = 'object-cover h-full',
enableImageActions = false,
...rest
} = props;
@ -37,10 +31,7 @@ export default function ImageWithFallback(props: ImageProps & {
const [hideFallback, setHideFallback] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
const viewerRef = useRef<Viewer | null>(null);
const imgRef = useRef<HTMLImageElement | null>(null);
const { isFullscreen } = useAppState();
const imgRef = useRef<HTMLImageElement>(null);
useEffect(() => {
const timeout = setTimeout(
@ -59,26 +50,6 @@ export default function ImageWithFallback(props: ImageProps & {
}
}, [isLoading, didError]);
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]);
const showFallback =
!wasCached &&
!hideFallback;
@ -94,65 +65,45 @@ export default function ImageWithFallback(props: ImageProps & {
};
return (
<>
<style jsx global>{` // Viewerjs customizations
.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,
'flex relative',
)}
ref={containerRef}
>
{(showFallback || shouldDebugImageFallbacks) &&
<div className={clsx(
'@container',
'absolute inset-0',
'overflow-hidden',
'transition-opacity duration-300 ease-in',
!(BLUR_ENABLED && blurDataURL) && 'bg-main',
(isLoading || shouldDebugImageFallbacks)
? 'opacity-100'
: 'opacity-0',
)}>
{(BLUR_ENABLED && blurDataURL)
? <img {...{
...rest,
src: blurDataURL,
className: clsx(
imgClassName,
getBlurClass(),
),
}} />
: <div className={clsx(
'w-full h-full',
'bg-gray-100/50 dark:bg-gray-900/50',
)} />}
</div>}
<Image
{...rest}
ref={imgRef}
priority={priority}
className={clsx(
imgClassName,
!isFullscreen && enableImageActions && 'cursor-zoom-in',
)}
onLoad={onLoad}
onError={onError}
/>
{enableImageActions && <FullscreenButton imageRef={imgRef} />}
<div
className={clsx(
className,
'flex relative',
)}
>
{(showFallback || shouldDebugImageFallbacks) &&
<div className={clsx(
'@container',
'absolute inset-0',
'overflow-hidden',
'transition-opacity duration-300 ease-in',
!(BLUR_ENABLED && blurDataURL) && 'bg-main',
(isLoading || shouldDebugImageFallbacks)
? 'opacity-100'
: 'opacity-0',
)}>
{(BLUR_ENABLED && blurDataURL)
? <img {...{
...rest,
src: blurDataURL,
className: clsx(
imgClassName,
getBlurClass(),
),
}} />
: <div className={clsx(
'w-full h-full',
'bg-gray-100/50 dark:bg-gray-900/50',
)} />}
</div>}
<Image {...{
...rest,
ref: imgRef,
priority,
className: imgClassName,
onLoad,
onError,
}} />
</div>
</>
);
}