diff --git a/src/app/App.tsx b/src/app/App.tsx index c41d9de..f8f9b2f 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -8,6 +8,7 @@ import { CreateEventModal } from '@/app/components/CreateEventModal' import { ExitOverlay } from '@/shared/components/ExitOverlay' import { useHorizontalWheelScroll } from '@/shared/hooks/useHorizontalWheelScroll' import { usePageTransition } from '@/shared/hooks/usePageTransition' +import { useKeyboardShortcuts } from '@/shared/hooks/useKeyboardShortcuts' import { HomeSection } from './sections/HomeSection' import { ProfileSection } from './sections/ProfileSection' @@ -55,6 +56,33 @@ export default function AppMain() { } }, []) + useKeyboardShortcuts({ + switchHome: () => { + const section = document.getElementById('home') + if (section) scrollSectionIntoView(section) + }, + + switchEvents: () => { + const section = document.getElementById('events') + if (section) scrollSectionIntoView(section) + }, + + switchOrgs: () => { + const section = document.getElementById('orgs') + if (section) scrollSectionIntoView(section) + }, + + switchProfile: () => { + const section = document.getElementById('profile') + if (section) scrollSectionIntoView(section) + }, + + openSearch: () => { + setSearchSessionKey((k) => k + 1) + setIsSearchOpen(true) + }, + }) + const scrollSectionIntoView = (section: HTMLElement) => { const scroller = scrollerRef.current if (!scroller) return diff --git a/src/app/components/AppTopNav.tsx b/src/app/components/AppTopNav.tsx index 2c926d1..112dd7b 100644 --- a/src/app/components/AppTopNav.tsx +++ b/src/app/components/AppTopNav.tsx @@ -34,7 +34,7 @@ export function AppTopNav({ onOpenSearch }: AppTopNavProps) { className={styles.searchButton} onClick={onOpenSearch} aria-label="Open search" - title="Search" + title="Search (CTRL+k)" > diff --git a/src/shared/components/TopNav.tsx b/src/shared/components/TopNav.tsx index 32cb631..f222f38 100644 --- a/src/shared/components/TopNav.tsx +++ b/src/shared/components/TopNav.tsx @@ -126,6 +126,7 @@ export function TopNav({ { linkRefs.current[item.href] = node }} diff --git a/src/shared/data/content.ts b/src/shared/data/content.ts index 35fa3ed..e1ed34a 100644 --- a/src/shared/data/content.ts +++ b/src/shared/data/content.ts @@ -1,6 +1,7 @@ export type NavItem = { label: string href: string + title?: string } export type FeatureCardModel = { @@ -28,10 +29,10 @@ export const navItems: NavItem[] = [ ] export const appNavItems: NavItem[] = [ - { label: 'profile', href: '#profile' }, - { label: 'home', href: '#home' }, - { label: 'events', href: '#events' }, - { label: 'orgs', href: '#orgs' }, + { label: 'profile', href: '#profile', title: 'Profile (P)' }, + { label: 'home', href: '#home', title: 'Home (H)' }, + { label: 'events', href: '#events', title: 'Events (E)' }, + { label: 'orgs', href: '#orgs', title: 'Organizations (O)' }, ] export const primaryCards: FeatureCardModel[] = [ diff --git a/src/shared/hooks/useKeyboardShortcuts.ts b/src/shared/hooks/useKeyboardShortcuts.ts new file mode 100644 index 0000000..bd523ac --- /dev/null +++ b/src/shared/hooks/useKeyboardShortcuts.ts @@ -0,0 +1,43 @@ +import { useEffect } from 'react' + +type ShortcutHandlers = { + switchHome: () => void + switchEvents: () => void + switchOrgs: () => void + switchProfile: () => void + openSearch: () => void +} + +function isTypingInInput(target: EventTarget | null) { + if (!(target instanceof HTMLElement)) return false + + const tag = target.tagName.toLowerCase() + + return tag === 'input' || tag === 'textarea' || target.isContentEditable +} + +export function useKeyboardShortcuts(handlers: ShortcutHandlers) { + useEffect(() => { + const onKeyDown = (event: KeyboardEvent) => { + if (isTypingInInput(event.target)) return + + if (event.ctrlKey && event.key === 'k') { + event.preventDefault() + handlers.openSearch() + return + } + + // Naviigation shortcuts + if (event.key === 'h') handlers.switchHome() + if (event.key === 'e') handlers.switchEvents() + if (event.key === 'o') handlers.switchOrgs() + if (event.key === 'p') handlers.switchProfile() + } + + window.addEventListener('keydown', onKeyDown) + + return () => { + window.removeEventListener('keydown', onKeyDown) + } + }, [handlers]) +} diff --git a/tsconfig.app.json b/tsconfig.app.json index f0b1ae5..b1e8bd0 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -24,6 +24,7 @@ "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true, "baseUrl": ".", + "ignoreDeprecations": "5.0", "paths": { "@/*": ["src/*"] }