Refactor escape handling

This commit is contained in:
Sam Becker 2025-01-26 16:42:47 -06:00
parent 637e5cd2d3
commit cae1da9f6a
6 changed files with 70 additions and 34 deletions

View File

@ -9,6 +9,7 @@ import AnimateItems from './AnimateItems';
import { PATH_ROOT } from '@/site/paths';
import usePrefersReducedMotion from '@/utility/usePrefersReducedMotion';
import useMetaThemeColor from '@/site/useMetaThemeColor';
import useEscapeHandler from '@/photo/useEscapeHandler';
export default function Modal({
onClosePath,
@ -55,6 +56,8 @@ export default function Modal({
},
});
useEscapeHandler(onClose, true);
return (
<motion.div
className={clsx(

View File

@ -1,10 +1,10 @@
import { useAppState } from '@/state/AppState';
import useKeydownHandler from '@/utility/useKeydownHandler';
import { RefObject, useCallback, useEffect, useRef } from 'react';
import Viewer from 'viewerjs';
const EVENT_SHOWN = 'shown';
const EVENT_HIDDEN = 'hidden';
const EVENT_KEYDOWN = 'keydown';
export default function useImageZoomControls(
imageRef: RefObject<HTMLDivElement | null>,
@ -13,7 +13,7 @@ export default function useImageZoomControls(
) {
const viewerRef = useRef<Viewer | null>(null);
const { isCommandKOpen, setShouldRespondToKeyboardCommands } = useAppState();
const { setShouldRespondToKeyboardCommands } = useAppState();
useEffect(() => {
if (imageRef.current && isEnabled) {
@ -67,21 +67,12 @@ export default function useImageZoomControls(
}, [imageRef, onHide]);
// On 'F' keydown, toggle fullscreen
const handleKeyDown = useCallback((e: KeyboardEvent) => {
if (
shouldExpandOnFKeydown &&
!isCommandKOpen &&
e.key.toUpperCase() === 'F'
) {
const handleKeyDown = useCallback(() => {
if (shouldExpandOnFKeydown) {
viewerRef.current?.show();
}
}, [shouldExpandOnFKeydown, isCommandKOpen]);
useEffect(() => {
document.addEventListener(EVENT_KEYDOWN, handleKeyDown);
return () => {
document.removeEventListener(EVENT_KEYDOWN, handleKeyDown);
};
}, [handleKeyDown]);
}, [shouldExpandOnFKeydown]);
useKeydownHandler(handleKeyDown, ['F']);
return {
open,

View File

@ -1,32 +1,22 @@
'use client';
import { getEscapePath } from '@/site/paths';
import { useAppState } from '@/state/AppState';
import { useRouter, usePathname } from 'next/navigation';
import { useEffect } from 'react';
const LISTENER_KEYUP = 'keyup';
import { useCallback } from 'react';
import useEscapeHandler from './useEscapeHandler';
export default function PhotoEscapeHandler() {
const router = useRouter();
const pathname = usePathname();
const { shouldRespondToKeyboardCommands } = useAppState();
const escapePath = getEscapePath(pathname);
useEffect(() => {
if (shouldRespondToKeyboardCommands) {
const onKeyUp = (e: KeyboardEvent) => {
if (e.key?.toUpperCase() === 'ESCAPE' && escapePath) {
router.push(escapePath, { scroll: false });
};
};
window.addEventListener(LISTENER_KEYUP, onKeyUp);
return () => window.removeEventListener(LISTENER_KEYUP, onKeyUp);
}
}, [shouldRespondToKeyboardCommands, router, escapePath]);
const escapeHandler = useCallback(() => {
if (escapePath) { router.push(escapePath, { scroll: false }); }
}, [escapePath, router]);
useEscapeHandler(escapeHandler);
return null;
}

View File

@ -0,0 +1,12 @@
import useKeydownHandler from '@/utility/useKeydownHandler';
export default function useEscapeHandler(
onEscape?: () => void,
ignoreShouldRespondToKeyboardCommands?: boolean,
) {
useKeydownHandler(
onEscape,
['ESCAPE'],
ignoreShouldRespondToKeyboardCommands,
);
}

View File

@ -4,7 +4,7 @@ import Modal from '@/components/Modal';
import { TbPhotoShare } from 'react-icons/tb';
import { clsx } from 'clsx/lite';
import { BiCopy } from 'react-icons/bi';
import { JSX, ReactNode } from 'react';
import { JSX, ReactNode, useEffect } from 'react';
import { shortenUrl } from '@/utility/url';
import { toastSuccess } from '@/toast';
import { PiXLogo } from 'react-icons/pi';
@ -24,7 +24,15 @@ export default function ShareModal({
socialText: string
children: ReactNode
}) {
const { setShareModalProps } = useAppState();
const {
setShareModalProps,
setShouldRespondToKeyboardCommands,
} = useAppState();
useEffect(() => {
setShouldRespondToKeyboardCommands?.(false);
return () => setShouldRespondToKeyboardCommands?.(true);
}, [setShouldRespondToKeyboardCommands]);
const renderIcon = (
icon: JSX.Element,

View File

@ -0,0 +1,32 @@
import { useAppState } from '@/state/AppState';
import { useCallback, useEffect } from 'react';
const LISTENER_KEYDOWN = 'keydown';
export default function useKeydownHandler(
onKeydown?: (e: KeyboardEvent) => void,
keys: string[] = [],
ignoreShouldRespondToKeyboardCommands?: boolean,
) {
const { shouldRespondToKeyboardCommands } = useAppState();
const onKeyUp = useCallback((e: KeyboardEvent) => {
if (keys.some(key => key.toUpperCase() === e.key?.toUpperCase())) {
onKeydown?.(e);
}
}, [onKeydown, keys]);
useEffect(() => {
if (
shouldRespondToKeyboardCommands ||
ignoreShouldRespondToKeyboardCommands
) {
window.addEventListener(LISTENER_KEYDOWN, onKeyUp);
return () => window.removeEventListener(LISTENER_KEYDOWN, onKeyUp);
}
}, [
shouldRespondToKeyboardCommands,
ignoreShouldRespondToKeyboardCommands,
onKeyUp,
]);
}