pseudo focus

This commit is contained in:
Manav Rathi
2025-03-06 09:09:15 +05:30
parent b1c680cccd
commit c96f2495ed
2 changed files with 63 additions and 30 deletions

View File

@@ -17,10 +17,7 @@ import { isDesktop } from "@/base/app";
import { SpacedRow } from "@/base/components/containers"; import { SpacedRow } from "@/base/components/containers";
import { DialogCloseIconButton } from "@/base/components/mui/DialogCloseIconButton"; import { DialogCloseIconButton } from "@/base/components/mui/DialogCloseIconButton";
import { useIsSmallWidth } from "@/base/components/utils/hooks"; import { useIsSmallWidth } from "@/base/components/utils/hooks";
import { import { type ModalVisibilityProps } from "@/base/components/utils/modal";
useModalVisibility,
type ModalVisibilityProps,
} from "@/base/components/utils/modal";
import { useBaseContext } from "@/base/context"; import { useBaseContext } from "@/base/context";
import { lowercaseExtension } from "@/base/file-name"; import { lowercaseExtension } from "@/base/file-name";
import { pt } from "@/base/i18n"; import { pt } from "@/base/i18n";
@@ -319,14 +316,11 @@ const FileViewer: React.FC<FileViewerProps> = ({
const [moreMenuAnchorEl, setMoreMenuAnchorEl] = const [moreMenuAnchorEl, setMoreMenuAnchorEl] =
useState<HTMLElement | null>(null); useState<HTMLElement | null>(null);
const [openImageEditor, setOpenImageEditor] = useState(false); const [openImageEditor, setOpenImageEditor] = useState(false);
const [openConfirmDelete, setOpenConfirmDelete] = useState(false);
const [openShortcuts, setOpenShortcuts] = useState(false);
const [isFullscreen, setIsFullscreen] = useState(false); const [isFullscreen, setIsFullscreen] = useState(false);
const { show: showConfirmDelete, props: confirmDeleteVisibilityProps } =
useModalVisibility();
const { show: showShortcuts, props: shortcutsVisibilityProps } =
useModalVisibility();
// Callbacks to be invoked (only once) the next time we get an update to the // Callbacks to be invoked (only once) the next time we get an update to the
// `files` or `favoriteFileIDs` props. // `files` or `favoriteFileIDs` props.
// //
@@ -359,10 +353,12 @@ const FileViewer: React.FC<FileViewerProps> = ({
return false; return false;
}); });
setOpenFileInfo(false); setOpenFileInfo(false);
setOpenImageEditor(false);
// No need to `resetMoreMenuButtonOnMenuClose` since we're closing // No need to `resetMoreMenuButtonOnMenuClose` since we're closing
// anyway and it'll be removed from the DOM. // anyway and it'll be removed from the DOM.
setMoreMenuAnchorEl(null); setMoreMenuAnchorEl(null);
setOpenImageEditor(false);
setOpenConfirmDelete(false);
setOpenShortcuts(false);
onClose(); onClose();
}, [onTriggerSyncWithRemote, onClose]); }, [onTriggerSyncWithRemote, onClose]);
@@ -424,10 +420,15 @@ const FileViewer: React.FC<FileViewerProps> = ({
return onDelete return onDelete
? () => { ? () => {
handleMoreMenuCloseIfNeeded(); handleMoreMenuCloseIfNeeded();
showConfirmDelete(); setOpenConfirmDelete(true);
} }
: undefined; : undefined;
}, [onDelete, showConfirmDelete, handleMoreMenuCloseIfNeeded]); }, [onDelete, handleMoreMenuCloseIfNeeded]);
const handleConfirmDeleteClose = useCallback(
() => setOpenConfirmDelete(false),
[],
);
// Not memoized since it uses the frequently changing `activeAnnotatedFile`. // Not memoized since it uses the frequently changing `activeAnnotatedFile`.
const handleDelete = async () => { const handleDelete = async () => {
@@ -626,8 +627,27 @@ const FileViewer: React.FC<FileViewerProps> = ({
const handleShortcuts = useCallback(() => { const handleShortcuts = useCallback(() => {
handleMoreMenuCloseIfNeeded(); handleMoreMenuCloseIfNeeded();
showShortcuts(); setOpenShortcuts(true);
}, [handleMoreMenuCloseIfNeeded, showShortcuts]); }, [handleMoreMenuCloseIfNeeded]);
const handleShortcutsClose = useCallback(() => setOpenShortcuts(false), []);
const shouldIgnoreKeyboardEvent = useCallback(() => {
// Don't handle keydowns if any of the modals are open.
return (
openFileInfo ||
moreMenuAnchorEl ||
openImageEditor ||
openConfirmDelete ||
openShortcuts
);
}, [
openFileInfo,
moreMenuAnchorEl,
openImageEditor,
openConfirmDelete,
openShortcuts,
]);
const performKeyAction = useCallback( const performKeyAction = useCallback(
(action): FileViewerPhotoSwipeDelegate["performKeyAction"] => { (action): FileViewerPhotoSwipeDelegate["performKeyAction"] => {
@@ -657,6 +677,7 @@ const FileViewer: React.FC<FileViewerProps> = ({
getFiles, getFiles,
isFavorite, isFavorite,
toggleFavorite, toggleFavorite,
shouldIgnoreKeyboardEvent,
performKeyAction, performKeyAction,
}; };
} }
@@ -667,8 +688,15 @@ const FileViewer: React.FC<FileViewerProps> = ({
delegate.getFiles = getFiles; delegate.getFiles = getFiles;
delegate.isFavorite = isFavorite; delegate.isFavorite = isFavorite;
delegate.toggleFavorite = toggleFavorite; delegate.toggleFavorite = toggleFavorite;
delegate.shouldIgnoreKeyboardEvent = shouldIgnoreKeyboardEvent;
delegate.performKeyAction = performKeyAction; delegate.performKeyAction = performKeyAction;
}, [getFiles, isFavorite, toggleFavorite, performKeyAction]); }, [
getFiles,
isFavorite,
toggleFavorite,
shouldIgnoreKeyboardEvent,
performKeyAction,
]);
// Notify the listeners, if any, for updates to files or favorites. // Notify the listeners, if any, for updates to files or favorites.
useEffect(() => { useEffect(() => {
@@ -822,7 +850,8 @@ const FileViewer: React.FC<FileViewerProps> = ({
</MoreMenu> </MoreMenu>
{/* TODO(PS): Fix imports */} {/* TODO(PS): Fix imports */}
<ConfirmDeleteFileDialog <ConfirmDeleteFileDialog
{...confirmDeleteVisibilityProps} open={openConfirmDelete}
onClose={handleConfirmDeleteClose}
onConfirm={handleDelete} onConfirm={handleDelete}
/> />
<ImageEditorOverlay <ImageEditorOverlay
@@ -831,7 +860,7 @@ const FileViewer: React.FC<FileViewerProps> = ({
file={activeAnnotatedFile?.file} file={activeAnnotatedFile?.file}
onSaveEditedCopy={handleSaveEditedCopy} onSaveEditedCopy={handleSaveEditedCopy}
/> />
<Shortcuts {...shortcutsVisibilityProps} /> <Shortcuts open={openShortcuts} onClose={handleShortcutsClose} />
</Container> </Container>
); );
}; };

View File

@@ -73,6 +73,14 @@ export interface FileViewerPhotoSwipeDelegate {
* > remain in the disabled state (until the file viewer is closed). * > remain in the disabled state (until the file viewer is closed).
*/ */
toggleFavorite: (annotatedFile: FileViewerAnnotatedFile) => Promise<void>; toggleFavorite: (annotatedFile: FileViewerAnnotatedFile) => Promise<void>;
/**
* Called when there is a keydown event, and our PhotoSwipe instance wants
* to know if it should ignore it or handle it.
*
* The delegate should return true when, e.g., the file info dialog is
* being displayed.
*/
shouldIgnoreKeyboardEvent: () => boolean;
/** /**
* Called when the user triggers a potential action using a keyboard * Called when the user triggers a potential action using a keyboard
* shortcut. * shortcut.
@@ -734,22 +742,18 @@ export class FileViewerPhotoSwipe {
const handleToggleFullscreen = () => const handleToggleFullscreen = () =>
delegate.performKeyAction("toggle-fullscreen"); delegate.performKeyAction("toggle-fullscreen");
pswp.on("keydown", (_e) => { pswp.on("keydown", (pswpEvent) => {
const e: KeyboardEvent = _e.originalEvent; // Ignore keyboard events when we do not have "focus".
if (delegate.shouldIgnoreKeyboardEvent()) {
pswpEvent.preventDefault();
return;
}
const e: KeyboardEvent = pswpEvent.originalEvent;
// Even though we ignore shift, Caps lock might still be on. // Even though we ignore shift, Caps lock might still be on.
const key = e.key.toLowerCase(); const key = e.key.toLowerCase();
console.log(
e,
key,
e.shiftKey,
e.altKey,
e.metaKey,
e.ctrlKey,
e.code,
);
// Keep the keybindings such that they don't use modifiers, because // Keep the keybindings such that they don't use modifiers, because
// these are more likely to interfere with browser shortcuts. // these are more likely to interfere with browser shortcuts.
// //