Consolidate event handling to zoom hook
This commit is contained in:
parent
2195379b74
commit
bbe49d3a0d
@ -1,69 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect, useCallback, RefObject } from 'react';
|
|
||||||
import { MdFullscreen, MdFullscreenExit } from 'react-icons/md';
|
|
||||||
import { clsx } from 'clsx/lite';
|
|
||||||
import { useAppState } from '@/state/AppState';
|
|
||||||
import LoaderButton from './primitives/LoaderButton';
|
|
||||||
|
|
||||||
export default function FullscreenButton({
|
|
||||||
className,
|
|
||||||
imageRef,
|
|
||||||
}: {
|
|
||||||
className?: string;
|
|
||||||
imageRef: RefObject<HTMLDivElement | null>;
|
|
||||||
}) {
|
|
||||||
const { isFullscreen, setIsFullscreen, isCommandKOpen } = useAppState();
|
|
||||||
|
|
||||||
// Toggle fullscreen mode
|
|
||||||
const toggleFullscreen = useCallback(async () => {
|
|
||||||
if (!document.fullscreenElement) {
|
|
||||||
if (isCommandKOpen) return;
|
|
||||||
await imageRef.current?.requestFullscreen();
|
|
||||||
setIsFullscreen?.(true);
|
|
||||||
} else {
|
|
||||||
await document.exitFullscreen();
|
|
||||||
setIsFullscreen?.(false);
|
|
||||||
}
|
|
||||||
}, [imageRef, setIsFullscreen, isCommandKOpen]);
|
|
||||||
|
|
||||||
// Toggle fullscreen on 'f' key press
|
|
||||||
const handleKeyDown = useCallback((event: KeyboardEvent) => {
|
|
||||||
if (event.key === 'f' || event.key === 'F') {
|
|
||||||
toggleFullscreen();
|
|
||||||
}
|
|
||||||
}, [toggleFullscreen]);
|
|
||||||
|
|
||||||
// Handle fullscreen change (e.g, switching tabs in fullscreen mode)
|
|
||||||
const handleFullscreenChange = useCallback(() => {
|
|
||||||
if (!document.fullscreenElement) {
|
|
||||||
setIsFullscreen?.(false);
|
|
||||||
}
|
|
||||||
}, [setIsFullscreen]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
document.addEventListener('keydown', handleKeyDown);
|
|
||||||
document.addEventListener('fullscreenchange', handleFullscreenChange);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('keydown', handleKeyDown);
|
|
||||||
document.removeEventListener('fullscreenchange', handleFullscreenChange);
|
|
||||||
};
|
|
||||||
}, [handleKeyDown, handleFullscreenChange]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LoaderButton
|
|
||||||
title="Toggle Fullscreen"
|
|
||||||
className={clsx(
|
|
||||||
className,
|
|
||||||
'text-medium absolute bottom-2 right-2 bg-white p-2 rounded',
|
|
||||||
'hidden md:block',
|
|
||||||
)}
|
|
||||||
icon={isFullscreen ? <MdFullscreenExit size={18} />
|
|
||||||
: <MdFullscreen size={18} />}
|
|
||||||
spinnerColor="light-gray"
|
|
||||||
styleAs="link"
|
|
||||||
onClick={toggleFullscreen}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,12 +1,19 @@
|
|||||||
import { RefObject, useEffect, useRef } from 'react';
|
import { useAppState } from '@/state/AppState';
|
||||||
|
import { RefObject, useCallback, useEffect, useRef } from 'react';
|
||||||
import Viewer from 'viewerjs';
|
import Viewer from 'viewerjs';
|
||||||
|
|
||||||
|
const EVENT_SHOWN = 'shown';
|
||||||
|
const EVENT_HIDDEN = 'hidden';
|
||||||
|
const EVENT_KEYDOWN = 'keydown';
|
||||||
|
|
||||||
export default function useImageZoomControls(
|
export default function useImageZoomControls(
|
||||||
imageRef: RefObject<HTMLDivElement | null>,
|
imageRef: RefObject<HTMLDivElement | null>,
|
||||||
isEnabled?: boolean,
|
isEnabled?: boolean,
|
||||||
) {
|
) {
|
||||||
const viewerRef = useRef<Viewer | null>(null);
|
const viewerRef = useRef<Viewer | null>(null);
|
||||||
|
|
||||||
|
const { isCommandKOpen, setShouldRespondToKeyboardCommands } = useAppState();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (imageRef.current && isEnabled) {
|
if (imageRef.current && isEnabled) {
|
||||||
viewerRef.current = new Viewer(imageRef.current, {
|
viewerRef.current = new Viewer(imageRef.current, {
|
||||||
@ -26,4 +33,41 @@ export default function useImageZoomControls(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [imageRef, isEnabled]);
|
}, [imageRef, isEnabled]);
|
||||||
|
|
||||||
|
// On shown, disable keyboard commands
|
||||||
|
const onShown = useCallback(() =>
|
||||||
|
setShouldRespondToKeyboardCommands?.(false),
|
||||||
|
[setShouldRespondToKeyboardCommands]);
|
||||||
|
useEffect(() => {
|
||||||
|
const imageRefCurrent = imageRef.current;
|
||||||
|
imageRefCurrent?.addEventListener(EVENT_SHOWN, onShown);
|
||||||
|
return () => {
|
||||||
|
imageRefCurrent?.removeEventListener(EVENT_SHOWN, onShown);
|
||||||
|
};
|
||||||
|
}, [imageRef, onShown]);
|
||||||
|
|
||||||
|
// On hide, reenable keyboard commands
|
||||||
|
const onHide = useCallback(() =>
|
||||||
|
setShouldRespondToKeyboardCommands?.(true),
|
||||||
|
[setShouldRespondToKeyboardCommands]);
|
||||||
|
useEffect(() => {
|
||||||
|
const imageRefCurrent = imageRef.current;
|
||||||
|
imageRefCurrent?.addEventListener(EVENT_HIDDEN, onHide);
|
||||||
|
return () => {
|
||||||
|
imageRefCurrent?.removeEventListener(EVENT_HIDDEN, onHide);
|
||||||
|
};
|
||||||
|
}, [imageRef, onHide]);
|
||||||
|
|
||||||
|
// On 'F' keydown, toggle fullscreen
|
||||||
|
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
||||||
|
if (!isCommandKOpen && e.key.toUpperCase() === 'F') {
|
||||||
|
viewerRef.current?.show();
|
||||||
|
}
|
||||||
|
}, [isCommandKOpen]);
|
||||||
|
useEffect(() => {
|
||||||
|
document.addEventListener(EVENT_KEYDOWN, handleKeyDown);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener(EVENT_KEYDOWN, handleKeyDown);
|
||||||
|
};
|
||||||
|
}, [handleKeyDown]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,9 +39,6 @@ export interface AppStateContext {
|
|||||||
setShouldDebugImageFallbacks?: Dispatch<SetStateAction<boolean>>
|
setShouldDebugImageFallbacks?: Dispatch<SetStateAction<boolean>>
|
||||||
shouldShowBaselineGrid?: boolean
|
shouldShowBaselineGrid?: boolean
|
||||||
setShouldShowBaselineGrid?: Dispatch<SetStateAction<boolean>>
|
setShouldShowBaselineGrid?: Dispatch<SetStateAction<boolean>>
|
||||||
// FULLSCREEN
|
|
||||||
isFullscreen?: boolean
|
|
||||||
setIsFullscreen?: Dispatch<SetStateAction<boolean>>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AppStateContext = createContext<AppStateContext>({});
|
export const AppStateContext = createContext<AppStateContext>({});
|
||||||
|
|||||||
@ -52,9 +52,6 @@ export default function AppStateProvider({
|
|||||||
useState(false);
|
useState(false);
|
||||||
const [shouldShowBaselineGrid, setShouldShowBaselineGrid] =
|
const [shouldShowBaselineGrid, setShouldShowBaselineGrid] =
|
||||||
useState(false);
|
useState(false);
|
||||||
// FULLSCREEN
|
|
||||||
const [isFullscreen, setIsFullscreen] =
|
|
||||||
useState(false);
|
|
||||||
|
|
||||||
const invalidateSwr = useCallback(() => setSwrTimestamp(Date.now()), []);
|
const invalidateSwr = useCallback(() => setSwrTimestamp(Date.now()), []);
|
||||||
|
|
||||||
@ -125,9 +122,6 @@ export default function AppStateProvider({
|
|||||||
setShouldDebugImageFallbacks,
|
setShouldDebugImageFallbacks,
|
||||||
shouldShowBaselineGrid,
|
shouldShowBaselineGrid,
|
||||||
setShouldShowBaselineGrid,
|
setShouldShowBaselineGrid,
|
||||||
// FULLSCREEN
|
|
||||||
isFullscreen,
|
|
||||||
setIsFullscreen,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user