Refactor escape handling
This commit is contained in:
parent
637e5cd2d3
commit
cae1da9f6a
@ -9,6 +9,7 @@ import AnimateItems from './AnimateItems';
|
|||||||
import { PATH_ROOT } from '@/site/paths';
|
import { PATH_ROOT } from '@/site/paths';
|
||||||
import usePrefersReducedMotion from '@/utility/usePrefersReducedMotion';
|
import usePrefersReducedMotion from '@/utility/usePrefersReducedMotion';
|
||||||
import useMetaThemeColor from '@/site/useMetaThemeColor';
|
import useMetaThemeColor from '@/site/useMetaThemeColor';
|
||||||
|
import useEscapeHandler from '@/photo/useEscapeHandler';
|
||||||
|
|
||||||
export default function Modal({
|
export default function Modal({
|
||||||
onClosePath,
|
onClosePath,
|
||||||
@ -55,6 +56,8 @@ export default function Modal({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEscapeHandler(onClose, true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { useAppState } from '@/state/AppState';
|
import { useAppState } from '@/state/AppState';
|
||||||
|
import useKeydownHandler from '@/utility/useKeydownHandler';
|
||||||
import { RefObject, useCallback, useEffect, useRef } from 'react';
|
import { RefObject, useCallback, useEffect, useRef } from 'react';
|
||||||
import Viewer from 'viewerjs';
|
import Viewer from 'viewerjs';
|
||||||
|
|
||||||
const EVENT_SHOWN = 'shown';
|
const EVENT_SHOWN = 'shown';
|
||||||
const EVENT_HIDDEN = 'hidden';
|
const EVENT_HIDDEN = 'hidden';
|
||||||
const EVENT_KEYDOWN = 'keydown';
|
|
||||||
|
|
||||||
export default function useImageZoomControls(
|
export default function useImageZoomControls(
|
||||||
imageRef: RefObject<HTMLDivElement | null>,
|
imageRef: RefObject<HTMLDivElement | null>,
|
||||||
@ -13,7 +13,7 @@ export default function useImageZoomControls(
|
|||||||
) {
|
) {
|
||||||
const viewerRef = useRef<Viewer | null>(null);
|
const viewerRef = useRef<Viewer | null>(null);
|
||||||
|
|
||||||
const { isCommandKOpen, setShouldRespondToKeyboardCommands } = useAppState();
|
const { setShouldRespondToKeyboardCommands } = useAppState();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (imageRef.current && isEnabled) {
|
if (imageRef.current && isEnabled) {
|
||||||
@ -67,21 +67,12 @@ export default function useImageZoomControls(
|
|||||||
}, [imageRef, onHide]);
|
}, [imageRef, onHide]);
|
||||||
|
|
||||||
// On 'F' keydown, toggle fullscreen
|
// On 'F' keydown, toggle fullscreen
|
||||||
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
const handleKeyDown = useCallback(() => {
|
||||||
if (
|
if (shouldExpandOnFKeydown) {
|
||||||
shouldExpandOnFKeydown &&
|
|
||||||
!isCommandKOpen &&
|
|
||||||
e.key.toUpperCase() === 'F'
|
|
||||||
) {
|
|
||||||
viewerRef.current?.show();
|
viewerRef.current?.show();
|
||||||
}
|
}
|
||||||
}, [shouldExpandOnFKeydown, isCommandKOpen]);
|
}, [shouldExpandOnFKeydown]);
|
||||||
useEffect(() => {
|
useKeydownHandler(handleKeyDown, ['F']);
|
||||||
document.addEventListener(EVENT_KEYDOWN, handleKeyDown);
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener(EVENT_KEYDOWN, handleKeyDown);
|
|
||||||
};
|
|
||||||
}, [handleKeyDown]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
open,
|
open,
|
||||||
|
|||||||
@ -1,32 +1,22 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { getEscapePath } from '@/site/paths';
|
import { getEscapePath } from '@/site/paths';
|
||||||
import { useAppState } from '@/state/AppState';
|
|
||||||
import { useRouter, usePathname } from 'next/navigation';
|
import { useRouter, usePathname } from 'next/navigation';
|
||||||
import { useEffect } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
import useEscapeHandler from './useEscapeHandler';
|
||||||
const LISTENER_KEYUP = 'keyup';
|
|
||||||
|
|
||||||
export default function PhotoEscapeHandler() {
|
export default function PhotoEscapeHandler() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
const { shouldRespondToKeyboardCommands } = useAppState();
|
|
||||||
|
|
||||||
const escapePath = getEscapePath(pathname);
|
const escapePath = getEscapePath(pathname);
|
||||||
|
|
||||||
useEffect(() => {
|
const escapeHandler = useCallback(() => {
|
||||||
if (shouldRespondToKeyboardCommands) {
|
if (escapePath) { router.push(escapePath, { scroll: false }); }
|
||||||
const onKeyUp = (e: KeyboardEvent) => {
|
}, [escapePath, router]);
|
||||||
if (e.key?.toUpperCase() === 'ESCAPE' && escapePath) {
|
|
||||||
router.push(escapePath, { scroll: false });
|
useEscapeHandler(escapeHandler);
|
||||||
};
|
|
||||||
};
|
|
||||||
window.addEventListener(LISTENER_KEYUP, onKeyUp);
|
|
||||||
return () => window.removeEventListener(LISTENER_KEYUP, onKeyUp);
|
|
||||||
}
|
|
||||||
}, [shouldRespondToKeyboardCommands, router, escapePath]);
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/photo/useEscapeHandler.ts
Normal file
12
src/photo/useEscapeHandler.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import useKeydownHandler from '@/utility/useKeydownHandler';
|
||||||
|
|
||||||
|
export default function useEscapeHandler(
|
||||||
|
onEscape?: () => void,
|
||||||
|
ignoreShouldRespondToKeyboardCommands?: boolean,
|
||||||
|
) {
|
||||||
|
useKeydownHandler(
|
||||||
|
onEscape,
|
||||||
|
['ESCAPE'],
|
||||||
|
ignoreShouldRespondToKeyboardCommands,
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@ import Modal from '@/components/Modal';
|
|||||||
import { TbPhotoShare } from 'react-icons/tb';
|
import { TbPhotoShare } from 'react-icons/tb';
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
import { BiCopy } from 'react-icons/bi';
|
import { BiCopy } from 'react-icons/bi';
|
||||||
import { JSX, ReactNode } from 'react';
|
import { JSX, ReactNode, useEffect } from 'react';
|
||||||
import { shortenUrl } from '@/utility/url';
|
import { shortenUrl } from '@/utility/url';
|
||||||
import { toastSuccess } from '@/toast';
|
import { toastSuccess } from '@/toast';
|
||||||
import { PiXLogo } from 'react-icons/pi';
|
import { PiXLogo } from 'react-icons/pi';
|
||||||
@ -24,7 +24,15 @@ export default function ShareModal({
|
|||||||
socialText: string
|
socialText: string
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
}) {
|
}) {
|
||||||
const { setShareModalProps } = useAppState();
|
const {
|
||||||
|
setShareModalProps,
|
||||||
|
setShouldRespondToKeyboardCommands,
|
||||||
|
} = useAppState();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setShouldRespondToKeyboardCommands?.(false);
|
||||||
|
return () => setShouldRespondToKeyboardCommands?.(true);
|
||||||
|
}, [setShouldRespondToKeyboardCommands]);
|
||||||
|
|
||||||
const renderIcon = (
|
const renderIcon = (
|
||||||
icon: JSX.Element,
|
icon: JSX.Element,
|
||||||
|
|||||||
32
src/utility/useKeydownHandler.ts
Normal file
32
src/utility/useKeydownHandler.ts
Normal 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,
|
||||||
|
]);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user