mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
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>
48 lines
1.2 KiB
TypeScript
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])
|
|
}
|