Files
metabuilder/hooks/email/useKeyboardShortcuts.ts
johndoe6345789 f22caa6e16 refactor(emailclient): full a11y pass, component decomposition, keyboard shortcuts
Accessibility:
- All components: data-testid, aria-label, aria-pressed, aria-current
- ComposeWindow: role=dialog, aria-modal, focus trap, Escape handler
- EmailCard: role=article, keyboard nav (Enter/Space), aria-current
- ThreadList: role=list with listitem wrappers, role=status empty state
- FolderNavigation: role=navigation, role=list, aria-current
- RecipientInput: role=group, aria-label per type, onKeyPress→onKeyDown
- BodyEditor: role=toolbar, aria-pressed on mode buttons
- StarButton: MaterialIcon (star/star_border), dynamic aria-label
- MarkAsReadCheckbox: dynamic label based on read state
- EmailHeader: role=banner, <time> element, data-testids

Component decomposition:
- Extract useEmailClient hook (state + callbacks)
- Extract demo-emails.ts (data constants)
- EmailClientContent.tsx: 267→127 LOC (composition only)

New: useKeyboardShortcuts hook (Gmail-style, 47 LOC)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:51:54 +00:00

48 lines
1.2 KiB
TypeScript

import { useEffect } from 'react'
/**
* Map of shortcut keys to handler functions.
* Use '#' for Shift+3, '?' for Shift+/, etc.
*/
export interface KeyboardShortcutMap {
[key: string]: () => void
}
const IGNORED_TAGS = new Set([
'INPUT', 'TEXTAREA', 'SELECT',
])
function isEditable(el: EventTarget | null): boolean {
if (!el || !(el instanceof HTMLElement)) return false
if (IGNORED_TAGS.has(el.tagName)) return true
return el.isContentEditable
}
/**
* Gmail-style keyboard shortcuts hook.
* Fires mapped actions on keydown unless an
* input/textarea/contenteditable is focused.
* @param shortcuts - key-to-action map
* @param enabled - toggle listener (default true)
*/
export function useKeyboardShortcuts(
shortcuts: KeyboardShortcutMap,
enabled = true
): void {
useEffect(() => {
if (!enabled) return
const handler = (e: KeyboardEvent) => {
if (isEditable(e.target)) return
const action = shortcuts[e.key]
if (action) {
e.preventDefault()
action()
}
}
window.addEventListener('keydown', handler)
return () => window.removeEventListener('keydown', handler)
}, [shortcuts, enabled])
}