feat: add comprehensive keyboard shortcuts system
Keyboard shortcuts: - **Ctrl/Cmd + O**: Open file dialog - **Ctrl/Cmd + Enter**: Start conversion - **Ctrl/Cmd + S**: Download results (ZIP if multiple) - **Ctrl/Cmd + R**: Reset converter - **Ctrl/Cmd + /**: Show keyboard shortcuts help - **?**: Show keyboard shortcuts help - **Escape**: Close shortcuts modal Implementation: - Created useKeyboardShortcuts custom hook - Platform-aware keyboard handling (Cmd on Mac, Ctrl elsewhere) - KeyboardShortcutsModal component with beautiful UI - Floating keyboard icon button (bottom-right) - Visual shortcut display with kbd tags - Context-aware shortcut execution (respects disabled states) - Prevents default browser behavior for shortcuts User experience: - Floating help button always accessible - Clean modal with shortcut descriptions - Platform-specific key symbols (⌘ on Mac) - Shortcuts disabled when modal is open - Clear visual feedback for shortcuts - Non-intrusive button placement Features: - Smart ref forwarding to FileUpload component - Conditional shortcut execution based on app state - Professional kbd styling for key combinations - Responsive modal with backdrop - Smooth animations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
65
lib/hooks/useKeyboardShortcuts.ts
Normal file
65
lib/hooks/useKeyboardShortcuts.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export interface KeyboardShortcut {
|
||||
key: string;
|
||||
ctrl?: boolean;
|
||||
alt?: boolean;
|
||||
shift?: boolean;
|
||||
description: string;
|
||||
action: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for managing keyboard shortcuts
|
||||
*/
|
||||
export function useKeyboardShortcuts(shortcuts: KeyboardShortcut[], enabled: boolean = true) {
|
||||
useEffect(() => {
|
||||
if (!enabled) return;
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
// Find matching shortcut
|
||||
const shortcut = shortcuts.find((s) => {
|
||||
const keyMatch = s.key.toLowerCase() === event.key.toLowerCase();
|
||||
const ctrlMatch = s.ctrl ? (event.ctrlKey || event.metaKey) : !event.ctrlKey && !event.metaKey;
|
||||
const altMatch = s.alt ? event.altKey : !event.altKey;
|
||||
const shiftMatch = s.shift ? event.shiftKey : !event.shiftKey;
|
||||
|
||||
return keyMatch && ctrlMatch && altMatch && shiftMatch;
|
||||
});
|
||||
|
||||
if (shortcut) {
|
||||
event.preventDefault();
|
||||
shortcut.action();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [shortcuts, enabled]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format shortcut key combination for display
|
||||
*/
|
||||
export function formatShortcut(shortcut: KeyboardShortcut): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
// Use Cmd on Mac, Ctrl on others
|
||||
const isMac = typeof window !== 'undefined' && /Mac|iPhone|iPod|iPad/.test(navigator.platform);
|
||||
|
||||
if (shortcut.ctrl) {
|
||||
parts.push(isMac ? '⌘' : 'Ctrl');
|
||||
}
|
||||
|
||||
if (shortcut.alt) {
|
||||
parts.push(isMac ? '⌥' : 'Alt');
|
||||
}
|
||||
|
||||
if (shortcut.shift) {
|
||||
parts.push(isMac ? '⇧' : 'Shift');
|
||||
}
|
||||
|
||||
parts.push(shortcut.key.toUpperCase());
|
||||
|
||||
return parts.join(' + ');
|
||||
}
|
||||
Reference in New Issue
Block a user