Move key commands to <AppViewSwitcher />, fix auto-focus tooltip issue
This commit is contained in:
parent
8a72e3d7ce
commit
63e843e2d6
@ -24,7 +24,6 @@ import AdminUploadPanel from '@/admin/upload/AdminUploadPanel';
|
||||
import { revalidatePath } from 'next/cache';
|
||||
import RecipeModal from '@/recipe/RecipeModal';
|
||||
import ThemeColors from '@/app/ThemeColors';
|
||||
import AppKeyListener from '@/app/AppKeyListener';
|
||||
|
||||
import '../tailwind.css';
|
||||
|
||||
@ -115,7 +114,6 @@ export default function RootLayout({
|
||||
</SwrConfigClient>
|
||||
<Analytics debug={false} />
|
||||
<SpeedInsights debug={false} />
|
||||
<AppKeyListener />
|
||||
<PhotoEscapeHandler />
|
||||
<ToasterWithThemes />
|
||||
</ThemeProvider>
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import useKeydownHandler from '@/utility/useKeydownHandler';
|
||||
import { PATH_FEED_INFERRED, PATH_GRID_INFERRED } from './paths';
|
||||
import { useCallback } from 'react';
|
||||
import { useRouter, usePathname } from 'next/navigation';
|
||||
|
||||
const PATH_MAPS = {
|
||||
G: PATH_GRID_INFERRED,
|
||||
F: PATH_FEED_INFERRED,
|
||||
};
|
||||
|
||||
export default function AppKeyListener() {
|
||||
const pathname = usePathname();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const onKeyDown = useCallback((e: KeyboardEvent) => {
|
||||
const path = PATH_MAPS[e.key.toLocaleUpperCase() as keyof typeof PATH_MAPS];
|
||||
if (path && pathname !== path) {
|
||||
router.push(path);
|
||||
}
|
||||
}, [router, pathname]);
|
||||
|
||||
useKeydownHandler({ onKeyDown });
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -12,7 +12,9 @@ import { GRID_HOMEPAGE_ENABLED } from './config';
|
||||
import AdminAppMenu from '@/admin/AdminAppMenu';
|
||||
import Spinner from '@/components/Spinner';
|
||||
import clsx from 'clsx/lite';
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import useKeydownHandler from '@/utility/useKeydownHandler';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
export type SwitcherSelection = 'feed' | 'grid' | 'admin';
|
||||
|
||||
@ -23,32 +25,39 @@ export default function AppViewSwitcher({
|
||||
currentSelection?: SwitcherSelection
|
||||
className?: string
|
||||
}) {
|
||||
const pathname = usePathname();
|
||||
|
||||
const {
|
||||
isUserSignedIn,
|
||||
isUserSignedInEager,
|
||||
setIsCommandKOpen,
|
||||
} = useAppState();
|
||||
|
||||
const hasAdminMenuOpenedOnce = useRef(false);
|
||||
const [isAdminMenuOpen, setIsAdminMenuOpen] = useState(false);
|
||||
const refHrefFeed = useRef<HTMLAnchorElement>(null);
|
||||
const refHrefGrid = useRef<HTMLAnchorElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (isAdminMenuOpen) {
|
||||
hasAdminMenuOpenedOnce.current = true;
|
||||
} else if (hasAdminMenuOpenedOnce.current) {
|
||||
// Blur admin menu button to avoid tooltip on dismiss
|
||||
setTimeout(() => {
|
||||
if (document.activeElement instanceof HTMLElement) {
|
||||
document.activeElement.blur();
|
||||
}
|
||||
}, 50);
|
||||
const onKeyDown = useCallback((e: KeyboardEvent) => {
|
||||
switch (e.key.toLocaleUpperCase()) {
|
||||
case 'F':
|
||||
if (pathname !== PATH_FEED_INFERRED) { refHrefFeed.current?.click(); }
|
||||
break;
|
||||
case 'G':
|
||||
if (pathname !== PATH_GRID_INFERRED) { refHrefGrid.current?.click(); }
|
||||
break;
|
||||
case 'A':
|
||||
if (isUserSignedIn) { setIsAdminMenuOpen(true); }
|
||||
break;
|
||||
}
|
||||
}, [isAdminMenuOpen]);
|
||||
}, [pathname, isUserSignedIn]);
|
||||
useKeydownHandler({ onKeyDown });
|
||||
|
||||
const [isAdminMenuOpen, setIsAdminMenuOpen] = useState(false);
|
||||
|
||||
const renderItemFeed =
|
||||
<SwitcherItem
|
||||
icon={<IconFeed includeTitle={false} />}
|
||||
href={PATH_FEED_INFERRED}
|
||||
hrefRef={refHrefFeed}
|
||||
active={currentSelection === 'feed'}
|
||||
tooltip={{
|
||||
content: 'Feed',
|
||||
@ -61,6 +70,7 @@ export default function AppViewSwitcher({
|
||||
<SwitcherItem
|
||||
icon={<IconGrid includeTitle={false} />}
|
||||
href={PATH_GRID_INFERRED}
|
||||
hrefRef={refHrefGrid}
|
||||
active={currentSelection === 'grid'}
|
||||
tooltip={{
|
||||
content: 'Grid',
|
||||
@ -93,7 +103,10 @@ export default function AppViewSwitcher({
|
||||
isOpen={isAdminMenuOpen}
|
||||
setIsOpen={setIsAdminMenuOpen}
|
||||
/>}
|
||||
tooltip={{ content: !isAdminMenuOpen ? 'Admin Menu' : undefined }}
|
||||
tooltip={{
|
||||
content: !isAdminMenuOpen ? 'Admin Menu' : undefined,
|
||||
keyCommand: !isAdminMenuOpen ? 'A' : undefined,
|
||||
}}
|
||||
noPadding
|
||||
/>}
|
||||
</Switcher>
|
||||
|
||||
@ -61,7 +61,10 @@ export default function Modal({
|
||||
},
|
||||
});
|
||||
|
||||
useEscapeHandler(onClose, true);
|
||||
useEscapeHandler({
|
||||
onKeyDown: onClose,
|
||||
ignoreShouldRespondToKeyboardCommands: true,
|
||||
});
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { clsx } from 'clsx/lite';
|
||||
import { SHOULD_PREFETCH_ALL_LINKS } from '@/app/config';
|
||||
import { ComponentProps, ReactNode } from 'react';
|
||||
import { ComponentProps, ReactNode, RefObject } from 'react';
|
||||
import Spinner from './Spinner';
|
||||
import LinkWithIconLoader from './LinkWithIconLoader';
|
||||
import Tooltip from './Tooltip';
|
||||
@ -11,6 +11,7 @@ export default function SwitcherItem({
|
||||
icon,
|
||||
title,
|
||||
href,
|
||||
hrefRef,
|
||||
className: classNameProp,
|
||||
onClick,
|
||||
active,
|
||||
@ -22,6 +23,7 @@ export default function SwitcherItem({
|
||||
icon: ReactNode
|
||||
title?: string
|
||||
href?: string
|
||||
hrefRef?: RefObject<HTMLAnchorElement | null>
|
||||
className?: string
|
||||
onClick?: () => void
|
||||
active?: boolean
|
||||
@ -57,6 +59,7 @@ export default function SwitcherItem({
|
||||
const content = href
|
||||
? <LinkWithIconLoader {...{
|
||||
href,
|
||||
ref: hrefRef,
|
||||
title,
|
||||
className,
|
||||
prefetch,
|
||||
@ -72,7 +75,7 @@ export default function SwitcherItem({
|
||||
? <Tooltip
|
||||
{...tooltip}
|
||||
classNameTrigger={WIDTH_CLASS}
|
||||
delayDuration={300}
|
||||
delayDuration={500}
|
||||
>
|
||||
{content}
|
||||
</Tooltip>
|
||||
|
||||
@ -75,6 +75,7 @@ export default function MoreMenu({
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content
|
||||
{...props}
|
||||
onCloseAutoFocus={e => e.preventDefault()}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={clsx(
|
||||
|
||||
@ -12,11 +12,11 @@ export default function PhotoEscapeHandler() {
|
||||
|
||||
const escapePath = getEscapePath(pathname);
|
||||
|
||||
const escapeHandler = useCallback(() => {
|
||||
const onKeyDown = useCallback(() => {
|
||||
if (escapePath) { router.push(escapePath, { scroll: false }); }
|
||||
}, [escapePath, router]);
|
||||
|
||||
useEscapeHandler(escapeHandler);
|
||||
useEscapeHandler({ onKeyDown });
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
import useKeydownHandler from '@/utility/useKeydownHandler';
|
||||
|
||||
export default function useEscapeHandler(
|
||||
onKeyDown?: (e: KeyboardEvent) => void,
|
||||
ignoreShouldRespondToKeyboardCommands?: boolean,
|
||||
args: Omit<Parameters<typeof useKeydownHandler>[0], 'keys'>,
|
||||
) {
|
||||
useKeydownHandler({
|
||||
onKeyDown,
|
||||
...args,
|
||||
keys: ['ESCAPE'],
|
||||
ignoreShouldRespondToKeyboardCommands,
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user