forked from akai/readest
fix(layout): show focus ring only for keyboard navigation (#2046)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import clsx from 'clsx';
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { navigateToLibrary, navigateToReader, showReaderWindow } from '@/utils/nav';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
@@ -105,6 +105,8 @@ const BookshelfItem: React.FC<BookshelfItemProps> = ({
|
||||
const { settings } = useSettingsStore();
|
||||
const { updateBook } = useLibraryStore();
|
||||
|
||||
const [keyboardFocused, setKeyboardFocused] = useState(false);
|
||||
|
||||
const showBookDetailsModal = useCallback(async (book: Book) => {
|
||||
if (await makeBookAvailable(book)) {
|
||||
handleShowDetailsBook(book);
|
||||
@@ -293,7 +295,7 @@ const BookshelfItem: React.FC<BookshelfItemProps> = ({
|
||||
[itemSelected],
|
||||
);
|
||||
|
||||
const { pressing, handlers } = useLongPress(
|
||||
const { pressing, hasPointerEventsRef, handlers } = useLongPress(
|
||||
{
|
||||
onLongPress: () => {
|
||||
handleSelectItem();
|
||||
@@ -323,6 +325,16 @@ const BookshelfItem: React.FC<BookshelfItemProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleFocus = () => {
|
||||
if (!hasPointerEventsRef.current) {
|
||||
setKeyboardFocused(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
setKeyboardFocused(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={clsx(mode === 'list' && 'sm:hover:bg-base-300/50 px-4 sm:px-6')}>
|
||||
<div
|
||||
@@ -330,7 +342,7 @@ const BookshelfItem: React.FC<BookshelfItemProps> = ({
|
||||
'group',
|
||||
mode === 'grid' && 'sm:hover:bg-base-300/50 flex h-full flex-col px-0 py-4 sm:px-4',
|
||||
mode === 'list' && 'border-base-300 flex flex-col border-b py-2',
|
||||
'focus-inset-2',
|
||||
keyboardFocused && 'focus-inset-2',
|
||||
appService?.isMobileApp && 'no-context-menu',
|
||||
pressing && mode === 'grid' ? 'scale-95' : 'scale-100',
|
||||
)}
|
||||
@@ -341,6 +353,8 @@ const BookshelfItem: React.FC<BookshelfItemProps> = ({
|
||||
transition: 'transform 0.2s',
|
||||
}}
|
||||
onKeyDown={handleKeyDown}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
{...handlers}
|
||||
>
|
||||
<div className='flex-grow'>
|
||||
|
||||
@@ -11,12 +11,14 @@ interface UseLongPressOptions {
|
||||
|
||||
interface UseLongPressResult {
|
||||
pressing: boolean;
|
||||
hasPointerEventsRef: React.MutableRefObject<boolean>;
|
||||
handlers: {
|
||||
onPointerDown: (e: React.PointerEvent) => void;
|
||||
onPointerUp: (e: React.PointerEvent) => void;
|
||||
onPointerMove: (e: React.PointerEvent) => void;
|
||||
onPointerCancel: (e: React.PointerEvent) => void;
|
||||
onPointerLeave: (e: React.PointerEvent) => void;
|
||||
onClick: (e: React.MouseEvent) => void;
|
||||
onContextMenu: (e: React.MouseEvent) => void;
|
||||
};
|
||||
}
|
||||
@@ -33,9 +35,11 @@ export const useLongPress = (
|
||||
deps: React.DependencyList,
|
||||
): UseLongPressResult => {
|
||||
const [pressing, setPressing] = useState(false);
|
||||
const timerRef = useRef<NodeJS.Timeout>();
|
||||
const timerRef = useRef<ReturnType<typeof setTimeout>>();
|
||||
const startPosRef = useRef<{ x: number; y: number } | null>(null);
|
||||
const pointerId = useRef<number | null>(null);
|
||||
const hasPointerEventsRef = useRef(false);
|
||||
const pointerEventTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
||||
const isLongPressTriggered = useRef(false);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
@@ -52,6 +56,9 @@ export const useLongPress = (
|
||||
return;
|
||||
}
|
||||
|
||||
hasPointerEventsRef.current = true;
|
||||
clearTimeout(pointerEventTimeoutRef.current);
|
||||
|
||||
pointerId.current = e.pointerId;
|
||||
startPosRef.current = { x: e.clientX, y: e.clientY };
|
||||
isLongPressTriggered.current = false;
|
||||
@@ -97,6 +104,10 @@ export const useLongPress = (
|
||||
}
|
||||
|
||||
reset();
|
||||
|
||||
pointerEventTimeoutRef.current = setTimeout(() => {
|
||||
hasPointerEventsRef.current = false;
|
||||
}, 100);
|
||||
},
|
||||
[onTap, moveThreshold, reset],
|
||||
);
|
||||
@@ -106,10 +117,20 @@ export const useLongPress = (
|
||||
if (e.pointerId !== pointerId.current) return;
|
||||
onCancel?.();
|
||||
reset();
|
||||
|
||||
pointerEventTimeoutRef.current = setTimeout(() => {
|
||||
hasPointerEventsRef.current = false;
|
||||
}, 100);
|
||||
},
|
||||
[onCancel, reset],
|
||||
);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
if (!hasPointerEventsRef.current) {
|
||||
onTap?.();
|
||||
}
|
||||
}, [onTap]);
|
||||
|
||||
const handleContextMenu = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
if (onContextMenu) {
|
||||
@@ -131,12 +152,14 @@ export const useLongPress = (
|
||||
|
||||
return {
|
||||
pressing,
|
||||
hasPointerEventsRef,
|
||||
handlers: {
|
||||
onPointerDown: handlePointerDown,
|
||||
onPointerUp: handlePointerUp,
|
||||
onPointerMove: handlePointerMove,
|
||||
onPointerCancel: handleCancel,
|
||||
onPointerLeave: handleCancel,
|
||||
onClick: handleClick,
|
||||
onContextMenu: handleContextMenu,
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user